年俸1000万の会社の試験問題 (二番煎じ)
少し前に話題になっていた、年収1000万の会社の試験問題をScalaで解く、の二番煎じ。
問題
Recruit - cgios technologies Inc.
4種類のアルファベット "A,C,G,T" から成るn文字の文字列のうち、
"AAG"という並びが含まれる文字列を全て列挙するプログラムを書きなさい。
ただし、nは3以上の整数とし、文字列内に同じアルファベットが出現しても構わないものとし、
出力順序は問わないものとします。
コード
これは自前で行った。
オーダーは悪い Θ(n) だが、n > 6 だとそもそも列挙する気が起きないのでコードの短さを優先。
import scala.math.BigInt
def pow(x: BigInt, y: Int): BigInt = if (y <= 0) 1 else x * pow(x, y - 1)
漸化式 + 無限Stream でカウント。
一度も "AAG" が出現しないパターンの場合の数を考える。
def count(n: Int): BigInt = {
lazy val a: Stream[BigInt] = (0 to 2).map(pow(4, _)).toStream #:::
(a zip a.drop(2) map { case (x, y) => 4 * y - x })
pow(4, n) - a(n)
}
2013-04-05追記: n が小さいので、単純な再帰でも十分計算可能。
def count(n: Int): BigInt = if (n < 3) 0 else count(n - 1) * 4 - count(n - 3) + pow(4, n - 3)
1文字を2ビットに圧縮し、ビット演算で列挙してみた。
def getList(n: Int): List[String] = {
def check(x: BigInt): Boolean = x match {
case _ if x == 0 => false
case _ if (x % 64) == 0x3e => true
case _ => check(x / 4)
}
def decode(n: Int)(x: BigInt): String = {
(n - 1 to 0 by -1).toList.map(i => "CTGA"(((x / pow(4, i)) & 3).toInt)).mkString("")
}
(BigInt(1) until pow(4, n)).toList.filter(check).map(decode(n))
}
// Count
(3 to 20) map count foreach println
// Enumerate
(3 to 6) map getList foreach println
実行結果
1
8
48
255
1268
6048
28033
127248
568480
2508031
10953452
47439632
204027713
872266264
3710060880
15709957631
66262531556
278519934528
List(AAG)
List(CAAG, TAAG, GAAG, AAGC, AAGT, AAGG, AAGA, AAAG)
List(CCAAG, CTAAG, CGAAG, CAAGC, CAAGT, CAAGG, CAAGA, CAAAG, TCAAG, TTAAG, TGAAG, TAAGC, TAAGT, TAAGG, TAAGA, TAAAG, GCAAG, GTAAG, GGAAG, GAAGC, GAAGT, GAAGG, GAAGA, GAAAG, ACAAG, ATAAG, AGAAG, AAGCC, AAGCT, AAGCG, AAGCA, AAGTC, AAGTT, AAGTG, AAGTA, AAGGC, AAGGT, AAGGG, AAGGA, AAGAC, AAGAT, AAGAG, AAGAA, AAAGC, AAAGT, AAAGG, AAAGA, AAAAG)
List(CCCAAG, CCTAAG, CCGAAG, CCAAGC, CCAAGT, CCAAGG, CCAAGA, CCAAAG, CTCAAG, CTTAAG, CTGAAG, CTAAGC, CTAAGT, CTAAGG, CTAAGA, CTAAAG, CGCAAG, CGTAAG, CGGAAG, CGAAGC, CGAAGT, CGAAGG, CGAAGA, CGAAAG, CACAAG, CATAAG, CAGAAG, CAAGCC, CAAGCT, CAAGCG, CAAGCA, CAAGTC, CAAGTT, CAAGTG, CAAGTA, CAAGGC, CAAGGT, CAAGGG, CAAGGA, CAAGAC, CAAGAT, CAAGAG, CAAGAA, CAAAGC, CAAAGT, CAAAGG, CAAAGA, CAAAAG, TCCAAG, TCTAAG, TCGAAG, TCAAGC, TCAAGT, TCAAGG, TCAAGA, TCAAAG, TTCAAG, TTTAAG, TTGAAG, TTAAGC, TTAAGT, TTAAGG, TTAAGA, TTAAAG, TGCAAG, TGTAAG, TGGAAG, TGAAGC, TGAAGT, TGAAGG, TGAAGA, TGAAAG, TACAAG, TATAAG, TAGAAG, TAAGCC, TAAGCT, TAAGCG, TAAGCA, TAAGTC, TAAGTT, TAAGTG, TAAGTA, TAAGGC, TAAGGT, TAAGGG, TAAGGA, TAAGAC, TAAGAT, TAAGAG, TAAGAA, TAAAGC, TAAAGT, TAAAGG, TAAAGA, TAAAAG, GCCAAG, GCTAAG, GCGAAG, GCAAGC, GCAAGT, GCAAGG, GCAAGA, GCAAAG, GTCAAG, GTTAAG, GTGAAG, GTAAGC, GTAAGT, GTAAGG, GTAAGA, GTAAAG, GGCAAG, GGTAAG, GGGAAG, GGAAGC, GGAAGT, GGAAGG, GGAAGA, GGAAAG, GACAAG, GATAAG, GAGAAG, GAAGCC, GAAGCT, GAAGCG, GAAGCA, GAAGTC, GAAGTT, GAAGTG, GAAGTA, GAAGGC, GAAGGT, GAAGGG, GAAGGA, GAAGAC, GAAGAT, GAAGAG, GAAGAA, GAAAGC, GAAAGT, GAAAGG, GAAAGA, GAAAAG, ACCAAG, ACTAAG, ACGAAG, ACAAGC, ACAAGT, ACAAGG, ACAAGA, ACAAAG, ATCAAG, ATTAAG, ATGAAG, ATAAGC, ATAAGT, ATAAGG, ATAAGA, ATAAAG, AGCAAG, AGTAAG, AGGAAG, AGAAGC, AGAAGT, AGAAGG, AGAAGA, AGAAAG, AACAAG, AATAAG, AAGCCC, AAGCCT, AAGCCG, AAGCCA, AAGCTC, AAGCTT, AAGCTG, AAGCTA, AAGCGC, AAGCGT, AAGCGG, AAGCGA, AAGCAC, AAGCAT, AAGCAG, AAGCAA, AAGTCC, AAGTCT, AAGTCG, AAGTCA, AAGTTC, AAGTTT, AAGTTG, AAGTTA, AAGTGC, AAGTGT, AAGTGG, AAGTGA, AAGTAC, AAGTAT, AAGTAG, AAGTAA, AAGGCC, AAGGCT, AAGGCG, AAGGCA, AAGGTC, AAGGTT, AAGGTG, AAGGTA, AAGGGC, AAGGGT, AAGGGG, AAGGGA, AAGGAC, AAGGAT, AAGGAG, AAGGAA, AAGACC, AAGACT, AAGACG, AAGACA, AAGATC, AAGATT, AAGATG, AAGATA, AAGAGC, AAGAGT, AAGAGG, AAGAGA, AAGAAC, AAGAAT, AAGAAG, AAGAAA, AAAGCC, AAAGCT, AAAGCG, AAAGCA, AAAGTC, AAAGTT, AAAGTG, AAAGTA, AAAGGC, AAAGGT, AAAGGG, AAAGGA, AAAGAC, AAAGAT, AAAGAG, AAAGAA, AAAAGC, AAAAGT, AAAAGG, AAAAGA, AAAAAG)
References
年俸1000万の会社の試験問題 - kencobaの日記