裏 RjpWiki

Julia ときどき R, Python によるコンピュータプログラム,コンピュータ・サイエンス,統計学

バラバラのマトリョーシカを一つに合体

2017年08月29日 | ブログラミング

バラバラのマトリョーシカを一つに合体

締め切りが 2017/08/29 10:00 AM なので,その 1 分後に投稿されるように予約

設問

大きさが異なる m 個のマトリョーシカ人形があります。
マトリョーシカ人形は、大きな人形の内部に、それより小さな人形を入れられます。

今、3つのテーブルの上に m 個のマトリョーシカ人形がバラバラに置かれています。
ただし、一つのテーブル上のマトリョーシカ人形は、必ず重ねて置かれています。
つまり、見えているのは最大で3つの人形です。

これらの人形からいずれか一つを選択して最も外側の人形を外し、
・ほかの人形に付け替える、
・他の人形が置かれていないテーブルに移す
のいずれかの作業を繰り返して、最短回数ですべての人形が一つのテーブルに揃うようにしたいと思います。
(つまり、一つの人形の中にすべての人形が入るようにする。)

ただし、一回に一つの人形しか作業できません。
当然、大きな人形の外側に小さな人形を付けることもできません。

標準入力から整数 m, n がスペース区切りで与えられたとき、ちょうど n 回で作業が完了するような初期配置が何通りあるかを求め、標準出力に出力してください。
なお、m, n はともに正の整数で、m < 15, n < 1600 を満たすものとします。

例えば、m=4のとき、最初に以下の左上の状態になっていると、図のように作業に6回かかります。

ちょうど6回で作業が完了するのは、以下の2通りですので、 m = 4, n = 6 のときは標準出力に2を出力します。

【入出力サンプル】
標準入力
4 6

標準出力
2

==================================================

ページの終わりに書くように,一行解がある。

m が 2 以上のときは,n = 1 が 1 通りずつ。
m が 3 以上のとき,水色の部分は m-1 のときの水色と黄色の部分を併せたものと同じ,黄色の部分はそれを 2 倍したもの。
2次元の配列にする必要はないが,わかりやすくするため。
実際には,ベクトルでよい。

f = function(m, n) {
    a = numeric(2^(m-1)-1)
    a[1] = 1
    b = a[1]
    for (M in 3:m) {
        begin1 = 2^(M-2)
        end1 = 3*2^(M-3)-1
        begin2 = 3*2^(M-3)
        end2 = 2^(M-1)-1
        a[begin1:end1] = b
        a[begin2:end2] = 2*b
        b = a[begin1:end2]
    }
    cat(a[n])
}
# arg = scan(file("stdin", "r"))
# f(arg[1], arg[2])

f(4, 6) # 2
f(5, 13) # 4
f(7,63) # 32
f(10, 499) # 64
f(12, 1599) # 128

いつものオンライン整数列大辞典にある。

オンライン整数列大辞典 https://oeis.org/A048896

引数 m は不要

f = function(n) {
    cat(2^(n-1-sum(floor(n/2^(1:10)))))
}

コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

同じ数字でサンドイッチ

2017年08月22日 | ブログラミング

同じ数字でサンドイッチ

締め切りが 2017/08/22 10:00 AM なので,その 1 分後に投稿されるように予約

1〜Nまでの数字が書かれたカードがそれぞれ2枚ずつと、ジョーカー1枚があります。
このカードを一列に並べるとき、書かれている数字の数だけ他のカードを挟んで並べることにします。
(ジョーカーは挟まれるだけで、挟む必要はありません。)

例えば、1, 2, 3のカードがそれぞれ2枚あるとき、 ジョーカーを「0」で表すと
2, 3, 1, 2, 1, 3, 0
2, 3, 0, 2, 1, 3, 1

などのように並べられます。
※いずれも1で挟まれたカードは1枚、2で挟まれたカードは2枚、3で挟まれたカードは3枚です。

N=5のときは、以下のような例で挟めます。
1, 5, 1, 2, 3, 4, 2, 5, 3, 0, 4
1, 4, 1, 5, 3, 0, 4, 2, 3, 5, 2


標準入力から N が与えられるとき、上記のように挟んだ並べ方の中から、ジョーカーが最も右側に来る並べ方を求め、その位置を標準出力に出力してください。
例えば、N=3, N=5 のときは上図のそれぞれ一行目が該当しますので、以下のように出力します。

Nは9以下の正の整数とし、上記のような挟み方が存在しない場合は0を出力してください。

【入出力サンプル1】
標準入力
3

標準出力
7

【入出力サンプル2】
標準入力
5

標準出力
10

============================================================

オンライン整数列大辞典の A279934
https://oeis.org/A279934

f = function(n) {
    m = 1:19
    a = (m-1)*((1+sqrt(5))/2)
    cat(m[a-floor(a) > 0.5][n])
}

#f(scan(file("stdin", "r"), quiet=TRUE))

f(3) # 7
f(4) # 9
f(5) # 10
f(8) # 17
f(9) # 18

以下の単純なプログラムで n の小さい方の解を求めてから

f = function(n) {
    library(e1071)
    a = c(0, rep(1:n, 2))
    p = permutations(length(a))
    maxPos = apply(p, 1, function(x) {
        b = a[x]
        ok = 1
        for (i in 1:n) {
            if (diff(grep(i, b))-1 != i) {
                ok = 0
                break
            }
        }
        if (ok == 1) {
            grep(0, b)
        } else {
            0
        }
    })
    cat(max(maxPos))
}



コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

トーナメントでの想定対戦数は?

2017年08月15日 | ブログラミング

トーナメントでの想定対戦数は?

締め切りが 2017/08/15 10:00 AM なので,その 1 分後に投稿されるように予約

設問

いよいよ夏の甲子園が始まります。
このような大会で用いられるのがトーナメント表です。
勝ち進んだことを想定し、どの投手をどの試合で使うのか検討することは珍しくありません。

今回はトーナメントにおける各チームの想定対戦数に注目します。
例えば、4チームでトーナメントを行うとき、トーナメント表の形として、以下のような形が考えられます。
このとき、各チームが決勝戦まで勝ち進んだときの対戦数は、それぞれ図の括弧内に書いた数字のようになります。



この想定対戦数の合計を考えると、左側の形は8、右側の形は9になります。
同様に、n チームでトーナメントを行うとき、この合計が最小になる形と最大になるような、トーナメント表の形を求めることにします。

標準入力から n が与えられたとき、考えられるトーナメント表の形について、想定対戦数の合計の「最小値」と「最大値」を標準出力にスペース区切りで出力してください。
なお、n は500以下の正の整数とします。
例えば、 n = 4 のとき、最小値は8、最大値は9ですので、以下のように出力します。

【入出力サンプル】
標準入力
4

標準出力
8 9

=================================================================

最大値となるのは,右の図のような最も不公平な一段階ずつ進行するトーナメント。
n が小さい方から数え上げると,n=2〜9 に対して,2, 5, 9, 14, 20, 27, 35, 44 のような階差数列で,一般項 an = (n-1)*(n+2)/2
最小値となるのは,チーム数が2の羃乗の場合は左のようなトーナメント。しかし,2の羃乗でない場合は,1回戦をパスするチームを作るトーナメント。
n=2〜9 に対して 2, 5, 8, 12, 16, 20, 24, 29 となり,オンライン整数列大辞典の "A003314: Binary entropy function"  https://oeis.org/A003314 である。

ということで,R で書くと以下のようになる。

f = function(n) {
    a = integer(n)
    a[1] = 0
    for (i in 2:n) {
        a[i] = i+a[floor(i/2)]+a[ceiling(i/2)]
    }
    cat(a[n], (n-1)*(n+2)/2)
}


#f(scan(file("stdin", "r")))
f(3) # 5 5
f(4) # 8 9
f(10) # 34 54
f(49) # 279 1224
f(500) # 4488 125249

コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

フィボナッチ進数

2017年08月11日 | ブログラミング

フィボナッチ進数

締め切りが 2017/08/11 10:00 AM なので,その 1 分後に投稿されるように予約

【概要】
二進数の1010111を十進数で表現する場合、各桁に 1, 2, 4, 8, 16, 32, 64, ... を掛けたものの総和で求められます。
つまり、
1×64+0×32+1×16+0×8+1×4+1×2+1×1
なので、
1010111(二進数) = 87(十進数)
になります。

で。
この問題は「フィボナッチ進数」という表現に関するものです。

フィボナッチ進数の場合は、各桁にフィボナッチ数1, 1, 2, 3, 5, 8, 13, 21, ...を掛けたものの総和と定義します(最初の0は使いません)。

たとえば 1010111 は
1×13+0×8+1×5+0×3+1×2+1×1+1×1
なので、
1010111(フィボナッチ進数) = 22(十進数)
となります。

1010111の他に、10000001, 1100010, 1100001なども 22 のフィボナッチ進数表現になります。

で。
数値(十進数)を与えます。
その値のフィボナッチ進数表現のうち、
それを二進数だと思ったときに最も大きな値になるものと最も小さな値になるものを計算して下さい。



【入出力】
入力は
22
のようになっています。普通に十進数です。

出力は、
10000010,1010111
のような感じです。

入力値のフィボナッチ進数表現のうち、
それを二進数だと思ったときに最も大きな値になるものと最も小さな値になるものを、
コンマ区切りでならべて下さい。

【例】
入力
出力
22
10000010,1010111
27
10010010,1101101
34
100000000,10101011

【補足】
    •    不正な入力に対処する必要はありません。
    •    入力値は、1以上 百万以下です。
    •    出力の左端の桁は必ず 1 にしてください。
    •    使う数字は0と1だけです。
    •    「フィボナッチ進数」は、この問題のために作られた造語です。

========================================================

f = function(n) {
    makeString = function(bit) { # 最下位桁から並んでいる bit を逆順にして,1 つの文字列に繋ぎ,先頭の 0 を取り除く
        sub("^0+", "", paste(rev(bit), collapse="")) # ベクトル [0, 1, 0, 0, 0, 0, 0, 1] は "10000010" になる
    }
    mx = 30 # n = 1000000 までなので,30 桁で十分(fib[30] = 832040, fib[31] = 1346269)
    bit = integer(mx) # 1 ... 30 桁
    fib = integer(mx) # フィボナッチ数列 fib[1] ... fib[30] = 1, 1, 2, 3, 5, 8, 13, 21, ..., 832040
    fib[1] = fib[2] = 1
    for (i in 3:mx) fib[i] = fib[i - 2] + fib[i - 1]
    for (i in mx:1) { # 上位桁から順に決めてゆく
        if (n >= fib[i]) {
            bit[i] = 1
            n = n - fib[i]
        }
    } # n = 22 ならば,21+0+0+0+0+0+1+0, 最も左が 1 の位として 0, 1, 0, 0, 0, 0, 0, 1
    max = bit # これが,二進数として読んだときの最大値を表すビット列
    # 最小値は,左から順に見ていって,"0, 0, 1" というシークエンスがあれば,それを "1, 1, 0" に置き換える
    # ただし,最も左の 2 桁については,"0,1" なら "1, 0" に置き換える
    # これを繰り返して,置き換えが起こらなかったら完了
    # 0, 1, 0, 0, 0, 0, 0, 1
    # 1, 0, 0, 0, 0, 1, 1, 0
    # 1, 0, 0, 1, 1, 0, 1, 0
    # 1, 1, 1, 0, 1, 0, 1, 0 完了
    repeat {
        complete = TRUE
        for (i in 2:mx) {
            if (bit[i] == 1) { # …+0+0+21+… は …+8+13+0+… と同じ
                if (i == 2 && bit[1] == 0) {
                    bit[2] = 0
                    bit[1] = 1
                } else if (i >= 3 && all(bit[i-1:2] == 0)) {
                    bit[i] = 0
                    bit[i-1:2] = 1
                }
                if (bit[i] == 0) { # 1 回でも置き換えが起きたらまだ未完成(の可能性あり)
                    complete = FALSE
                }
            }
        }
        if (complete) { # 1 回も置き換えが起きなければ完成
            break
        }
    }
    min = bit
    cat(sprintf("%s,%s\n", makeString(max),makeString(min)))
}
# f(scan(file("stdin", "r")))

> f(1000000)
100010100000000000101000000000,10111110101010101111101010101
> f(999999)
100010100000000000100101010100,10111110101010101111011111111
> f(1)
10,1
> f(2)
100,11
> f(3)
1000,101
> f(832040)
100000000000000000000000000000,10101010101010101010101010101
> f(832039)
10101010101010101010101010100,1111111111111111111111111111
> f(129177)
10000010001000000010101000,1010111011101010111111101
> f(636488)
10010000000000101000010000100,1101010101011011101101011011
> f(325039)
1000000010000010001010100100,101010111010111011111111011
> f(77037)
1000000010010000100001010,101010111101011010110111
> f(825561)
10101010100000001000101010010,1111111110101011101111111101

コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

「キャンディ・アンド・チョコレート」問題

2017年08月10日 | ブログラミング

「キャンディ・アンド・チョコレート」問題

締め切りが 2017/08/10 10:00 AM なので,その 1 分後に投稿されるように予約

設問

n 個のキャンディをグループに分けます。
グループの最大のキャンディの個数が k 個となるような分け方の数を F(n, k) と定義します。
 
例えば、F(8, 3)=5 です。このときの分け方を以下に示します。
なお個々のキャンディを区別せずに扱う点に注意してください。

同様に、F(4, 2)=2,F(20, 6)=90 となることが確かめられます。

次に、n 個のチョコレートをグループに分けます。
グループの数がちょうど k 個となるような分け方の数を G(n, k) と定義します。
 
例えば、G(9, 4)=6 です。このときの分け方を以下に示します。

同様に、G(4, 2)=2,G(18, 4)=47 となることが確かめられます。

標準入力から、自然数 n と k (1 ≦ k ≦ n ≦ 100)が半角空白区切りで与えられます。
標準出力に F(n, k)+G(n, k) の値を出力するプログラムを書いてください。
 
例えば入力が「4 2」の場合は、F(4, 2)+G(4, 2) の値である「4」を出力してください。
 
=======================================================

またも整数分割法である。R でシンプルに書く。

initDiv = function(length, max) {
    ary = NULL
    repeat {
        if (max >= length) {
            ary = c(ary, length)
            return(ary)
        } else {
            ary = c(ary, max)
            length = length - max
        }
    }
}

nextDiv = function(ary) {
    repeat { # 1でない要素を後ろから探す
        sum = 0
        for (pos in length(ary):1) {
            a = ary[pos]
            sum = sum + a
            if (a != 1) {
                break
            }
        }
        if (ary[1] == 1) { # 全て1
            return(FALSE)
        }
        ary = ary[1:pos]
        ary[pos] = ary[pos] - 1
        max = ary[pos]
        sum = sum - max
        pos = pos + 1
        repeat {
            if (max >= sum) {
                ary[pos] = sum

                return(ary)
            } else {
                ary[pos] = max
                sum = sum - max
            }
            pos = pos + 1
        }
    }
}

F = function(length, max) {
    first = TRUE
    count = 0
    repeat {
        if (first) {
            d = initDiv(length, max)
            first = FALSE
        } else {
            d = nextDiv(d)
            if (length(d) == 1) {
                break
            }
        }
        if (d[1] == max) {
            count = count+1
        } else {
            break
        }
    }
    count
}

G = function(length, max) {
    first = TRUE
    count = 0
    repeat {
        if (first) {
            d = initDiv(length, length)
            first = FALSE
        } else {
            d = nextDiv(d)
            if (length(d) == 1) {
                break
            }
        }
        if (length(d) == max) {
            count = count+1
        }
    }
    count
}

少しやってみると,F(n, k) = G(n, k) であることがわかる。
上のプログラムでは F は G より実行時間が短いので,F の結果を 2 倍すればよいが,それでも n, k によっては実行時間 1 秒という制限にかかる。

よって,n, k の小さい場合についての結果から漸化式を求める。

f = function(n, m) {
    a = matrix(0, n, m)
    a[,2] = (1:n %/% 2)*2
    for (j in 3:m) {
        a[j:min(n, 2*j-1), j] = a[j:min(n, 2*j-1)-1, j-1]
        for (i in min(n, 2*j):n) {
            a[i, j] = a[i-1, j-1] + a[i-j, j]
        }
    }
    a[n, m]
}

爆速である。

> f(20, 7) # 164
[1] 164
> f(35, 7) # 2734
[1] 2734
> f(55, 45) # 84
[1] 84
> f(60, 10) # 125480
[1] 125480
> f(98, 40) # 1428016
[1] 1428016
> f(100, 18) # 22175656
[1] 22175656


コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

〇×ゲームの手順は何通り?

2017年08月08日 | ブログラミング

〇×ゲームの手順は何通り?

締め切りが 2017/08/08 10:00 AM なので,その 1 分後に投稿されるように予約

設問

3×3のマス目を使って〇×ゲーム(三目並べ)を行います。
2人が交互に〇と×を空いているマスに記入していき、同じ記号が縦、横、斜めのいずれかに3つ並ぶとその記号の人が勝ちです。
すべてのマスが埋まっても3つ並ばないとき、その勝負は引き分けとなります。

例えば、以下の左図のような順に書いていったとき、×が横に3つ並んだので×の勝ちとなります。
一方、右図のような順に書いていったとき、いずれも3つ並ばないため、引き分けとなります。
(必ず〇の人から始め、勝負が決まった時点で終了するものとします)

標準入力から整数 n が与えられたとき、ちょうど n 手でどちらか一方が勝つまでの手順が何通りあるかを求めてください。
なお、n は5以上9以下の整数です。

例えば、n = 5 のとき、〇の配置は、以下の8通りが考えられます。
それぞれについて、残りのマスのうち2箇所に×が入るため、手順を考えると全部で1440通りあります。

【入出力サンプル】
標準入力
5

標準出力
1440

================================================================

R では,惜しいところで計算時間オーバーになるので,Java で書き直すという,いつものパターン。
R では,permutations と combn を組み合わせて全ての場合をもうらする。
Java では,permutation の結果の unique  をとっていない(unique は計算時間がかかる)ので,n = 5, 6, 7 のときには重複してカウントすることになるので,その調整をする。

================================================================

R

permutations = function(n) {
    if (n == 1)
        return(matrix(1))
    else if (n < 2)
        stop("n must be a positive integer")
    z = matrix(1)
    for (i in 2:n) {
        x = cbind(z, i)
        a = c(1:i, 1:(i - 1))
        z = matrix(0, ncol = ncol(x), nrow = i * nrow(x))
        z[1:nrow(x), ] = x
        for (j in 2:i - 1) {
            z[j * nrow(x) + 1:nrow(x), ] = x[, a[1:i + j]]
        }
    }
    dimnames(z) = NULL
    z
}

f = function(n) {
    count = 0
    p = permutations(n)
    b = combn(9, n)
    for (i in 1:ncol(b)) {
        a = matrix((b[,i])[p], ncol=n)
        for (j in seq_len(nrow(a))) {
            x = a[j, ]
            board = integer(9)
            for (k in 1:9) {
                if (k%%2) {
                    board[x[k]] = 1
                } else {
                    board[x[k]] = 10
                }
                if (k >= 5) {
                    b1 = board[1] + board[2] + board[3]
                    b2 = board[4] + board[5] + board[6]
                    b3 = board[7] + board[8] + board[9]
                    b4 = board[1] + board[4] + board[7]
                    b5 = board[2] + board[5] + board[8]
                    b6 = board[3] + board[6] + board[9]
                    b7 = board[1] + board[5] + board[9]
                    b8 = board[3] + board[5] + board[7]
                    if (b1 == 3  || b2 == 3  || b3 == 3  || b4 == 3  ||
                        b5 == 3  || b6 == 3  || b7 == 3  || b8 == 3  ||

                        b1 == 30 || b2 == 30 || b3 == 30 || b4 == 30 ||
                        b5 == 30 || b6 == 30 || b7 == 30 || b8 == 30) {

                        count = count + (k == n)
                        break
                    }
                }
            }
        }
    }
    cat(count)
}

system.time(f(5)) # 1440, 0.268sec.
system.time(f(6)) # 5328,0.739sec.
system.time(f(7)) # 47952, 1.928sec.
system.time(f(8)) # 72576,3.516sec.
system.time(f(9)) # 81792, 3.812sec.

================================================================

Java

import java.util.Scanner;

public class Main {

    public static void main(String[] args) {
        long end;
        long start = System.currentTimeMillis();
        int i, j;
        int b1, b2, b3, b4, b5, b6, b7, b8;
        int n;
        int[][] z = permutations(9);
        int sizeZ = factorial(9);
        int count = 0;
        if (true) {
            Scanner cin = new Scanner(System.in);
            String line;
            line = cin.nextLine();
            n = Integer.parseInt(line);
        } else {
            n = 9;
        }
        for (i = 1; sizeZ >= i; i++) {
            int[] board = new int[10];
            for (j = 1; n >= j; j++) {
                if (j % 2 == 0) {
                    board[z[i][j]] = 1;
                } else {
                    board[z[i][j]] = 10;
                }
                if (j >= 5) {
                    b1 = board[1] + board[2] + board[3];
                    b2 = board[4] + board[5] + board[6];
                    b3 = board[7] + board[8] + board[9];
                    b4 = board[1] + board[4] + board[7];
                    b5 = board[2] + board[5] + board[8];
                    b6 = board[3] + board[6] + board[9];
                    b7 = board[1] + board[5] + board[9];
                    b8 = board[3] + board[5] + board[7];
                    if (b1 == 3  || b2 == 3  || b3 == 3  || b4 == 3  ||
                        b5 == 3  || b6 == 3  || b7 == 3  || b8 == 3  ||

                        b1 == 30 || b2 == 30 || b3 == 30 || b4 == 30 ||
                        b5 == 30 || b6 == 30 || b7 == 30 || b8 == 30) {

                        if (j == n) {
                            count++;
                        }
                        break;
                    }
                }
            }
        }
        System.out.println(count / factorial(9-n));
    }

    static int factorial(int n) {
        int i, result = 1;
        for (i = 1; n >= i; i++) {
            result *= i;
        }
        return result;
    }

    static int[][] permutations(int n) {
        int sizeZ = factorial(n);
        int sizeX = sizeZ / (n - 1);
        int[][] z = new int[sizeZ + 1][n + 1];
        int[][] x = new int[sizeX + 1][n + 1];
        int nrowZ, ncolZ, nrowX, ncolX;
        int i, i2, j, j2;
        z[1][1] = 1;
        nrowZ = ncolZ = 1;
        for (i = 2; n >= i; i++) {
            for (i2 = 1; nrowZ >= i2; i2++) {
                for (j2 = 1; ncolZ >= j2; j2++) {
                    x[i2][j2] = z[i2][j2];
                }
                x[i2][ncolZ + 1] = i;
            }
            nrowX = nrowZ;
            ncolX = ncolZ + 1;
            for (j = 1; i >= j; j++) {
                for (j2 = 1; nrowX >= j2; j2++) {
                    for (i2 = 1; ncolX >= i2; i2++) {
                        z[(j - 1) * nrowX + j2][i2] = x[j2][(j + i2 - 2) % i + 1];
                    }
                }
            }
            nrowZ = i * nrowX;
            ncolZ = ncolX;
        }
        return z;
    }

}

コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

PVアクセスランキング にほんブログ村

PVアクセスランキング にほんブログ村