3S
  • Portfolio Top
  • Categories
  • Tags
  • Archives

生物情報ソフトウエア論Ⅰ 2018年解答

2018年 解答

テスト対策と言うことで2018年の過去問(講義(Web: link)に載っている)を解いたので、その解答をまとめました。

問題1

1

  1. i - left
  2. right - i
  3. i-1
  4. left=i+1
  5. i+1
  6. right=i-1

最終的なプログラムは以下のようになる。

void randomQuickSort3(int* target, int aLeft, int aRight) {
  int left = aLeft; int right = aRight;
  while (left < right) {
    int i = partition(target, left, right);
    if( i - left <= right - i ){ // The left interval is shorter.
      randomQuickSort3(target, left, i-1);
      left=i+1;
    }else{                       // The right interval is shorter.
      randomQuickSort3(target, i+1, right);
      right=i-1;
    }
  }
}

この時、入力配列 target の長さが2^40のように長い配列でも stack overflow を起こさず動作する理由は、「短い部分列を先に処理することで深さを \(\log_2n\) 以下に抑えているから。」です。  関数呼出しの際には、パラメータや命令のアドレス、局所変数などをスタックに積み(push)、関数の実行が終わると取り出されます(pop)。  したがって、関数の中で関数を並列に呼ぶ分には問題はないのですが、直列に(こんな表現は多分正しくないです笑)呼ぶとその度にスタックに積まなくてはならず、メモリ領域が足りなくなり、stack overflowを起こしてしまいます。  ゆえに、再帰の深さを \(\log_2n\) 以下に抑えている上記のプログラムはstack overflowを起こしにくいのです。

2

下からk番目の要素の場合、以下のようになる。(いわゆるQuick select)k番目が含まれている配列のみをソート(partition)していく。

int select(int* target, int aLeft, int aRight, int k) {
  int left = aLeft; int right = aRight;
  if(left < right) {
    int i = partition(target, left, right);
    if(i == k){
      return target[k];
    else if( i < k ){
      return select(target, i+1, right, k);
    }else{ // i > k
      return select(target, left, i-1, k);
    }
  }
}

3

k番目の要素を選択する平均計算量を \(T(n,k)\) と表記し、最悪の場合でも上界が \(O(n)\) であることを示す。

まず、k番目の要素を選択する平均計算量を \(C(n)\) と表す。partition の結果 \(i\) 個と \(n-i\) 個の要素に分かれる確率を \(P(i,n)\) とすると、\(C(n)\) は \(i\) が \(1\sim n-1\) を動いた場合の期待値と考えられるので、

$$C(n) = \sum_{i=1}^{n-1}P(i,n)\left\{cn + \frac{i}{n}C(i) + \frac{n-i}{n}C(n-i)\right\}$$

で与えられる。ここで、\(P(i,n)\) は pivot がちょうど \(i+1\) 番目の要素である確率であり、与えられた列の先頭2つに \(i\) 以下の要素とちょうど \(i+1\) 番目の要素が位置している確率であるので、\(2i/n(n-1)\) となる。これを代入して整理すると、

$$ \begin{aligned} C(n) &= \sum_{i=1}^{n-1}\frac{2i}{n(n-1)}\left\{cn + \frac{i}{n}C(i) + \frac{n-i}{n}C(n-i)\right\}\\ &= cn + \frac{2}{n^2(n-1)}\sum_{i=1}^{n-1}\left\{i^2C(i) + i(n-i)C(n-i)\right\}\\ &= cn + \frac{2}{n^2(n-1)}\left\{\sum_{i=1}^{n-1}niC(i) - \sum_{i=1}^{n-1}(n-i)iC(i) + \sum_{i=1}^{n-1}i(n-i)C(n-i)\right\}\\ &= cn + \frac{2}{n(n-1)}\sum_{i=1}^{n-1}iC(i) \end{aligned} $$

 となる。この \(C(n)\) の計算量を示す方法として、解を仮定してその正しさを帰納法で示す方法をとる。まず、\(C(n)\leq an\) を仮定する。  \(n=2\) の時は、\(C(n) = 2c + C(1)\) であるので、\(a=c+C(1)\) とすれば成立する。  \(n=1,\ldots,k-1\) の時に成立したとして、\(n=k\) で成立することを示す。

$$ \begin{aligned} C(k) &= ck + \frac{2}{k(k-1)}\sum_{i=1}^{n-1}iC(i)\\ &\leq ck + \frac{2}{k(k-1)}\sum_{i=1}^{k-1}ai^2\\ &= ck + \frac{2}{k(k-1)}\left(\frac{1}{2}ak(k-1)\right)\\ &= ck + a\\\end{aligned} $$

 したがって、\(a>c\) とすれば、\(C(k)\leq ak\) が示せたことになる。  以上により、\(a > c+C(1)\) とすれば、全ての \(n\) で \(C(k)\leq ak\) が示せたことになり、クイックセレクトの平均計算量が \(O(n)\) になることが示せた。

問題2

1

項目 定義
suffix array SA Sの全てのsuffixを辞書式順序でソートした時に、i番目(0<=i<n)にランクされたsuffixS[k,n)の開始位置kを、SA[i]=kと記録する。(開始位置kだけ記録すればSからsuffixの文字列は得られるので問題ない。)
inverse suffix array ISA "SA[i]=k"ならば"ISA[k]=i"と記録したもの。suffix S[k,n)が何番目かを調べるための補助的データ構造。
h-順序 最初のh文字だけでソートしたsuffixの順序
近似 suffix array 最初のh文字だけで作成したsuffix array
近似 inverse suffix array 最初のh文字だけで作成したinverse suffix array

2

基本的なアイデアは、「h文字目までの順番が確定している時、その順番を利用すればh+h=2h文字目までの順番も分かる」ということ。詳しくはここで図を用いて解説した。

3

データの順序を交換する過程を、h文字目までが同じものを1つのノードとした木構造で表現する。ここで、pivotの値を持つnodeを"pivot node(=pivot)"、それ以外を"non-pivot node(> or < pivot)"と呼ぶ。

ここで、各反復時には全ての要素を見るので、O(nlog(最大反復長))の時間がかかる。(すでにソートされている部分はスキップするので、次第に小さくなるが)

この時、pivotに中央値を選べば"non-pivot node"のデータ数は親ノードの半分未満となる。したがって、木の根から各葉へのパス上に"pivot node", "non-pivot node"はlog_2n+1以下となる。各ノードで中央値を線形時間で計算でき、その中央値をpivotとして分割するのも線形時間なので、各列の最悪計算量はO(n)  ゆえに、最悪計算量はO(nlogn)

4

doubling法を実装した場合、suffixの最初のh文字だけをソートすることを繰り返す。

例えばAだけからなる長さnの記号列を入力として与えたことを考える。この時、同じ値をまとめるTernary-split quick sortを使わず、最悪時間計算量O(nlogn)のマージソートなどを使うと、h=1,2,4,...に対して毎回O(nlogn)かかるため、総計算量がO(n(logn)^2)になってしまう。  一方で、Ternary-split quick sortを使えば、この状況を回避できる。

問題3

 Suffix Array に関しては、DoublingやSA-ISに関してアルゴリズムの課題時にまとめたので、省略。

問題4

1

i 0 1 2 3 4 5 6 7 8 9 10 11
BWT[i] A A A A A C $ A A A A A

2

  • suffix arrayの先頭文字の直前の文字を列にしたのがBWT
  • T[i]は、SA[i]-1番目(つまり、BWT[i])の文字のsuffixのランキングを表している。
  • BWTの順番もsuffix arrayの順番と対応しているので、BWT[T[i]]には、BWT[i]のSでの直前の文字が書いてある。
  • 最後の文字は"$"であり必要ないので、その1つ前の文字(つまりS[n-2]であり、BWT[0])から直前の文字を再帰的に復元できる。

以上より、以下の式でSを復元できる。

$$S[n-2-k]=B W T\left[T^{k}[0]\right](k=0,1, \ldots, n-2) \\ T[i]=I S A[S A[i]-1]$$

3

BWT[i]はS[SA[i]-1]を記録しており、元の文字列よりも連続した値が続きやすいという性質を持つ。すると、ランレングス圧縮などをかけて文字列を圧縮しやすくなるから。

4

T[i]もBWT[i]も、iはsuffix arrayの順にソートされている。ここで、T[i]はBWT[i]の文字のsuffixのランキングを表しているので、その値はBWT[i]の文字情報だけで何番目なのか(累積和をとる, C[x])にBWT[i]の文字内で何番目なのか(Occ(x,i))を足せば良い(0-origin indexingなので-1する)

5

$$ \begin{aligned} l b(x W) &=C(x)+\operatorname{Occ}(x, l b(W)-1) & (1)\\ u b(x W) &=C(x)+\operatorname{Occ}(x, u b(W))-1 & (2) \end{aligned} $$
  1. xから始まるxWより小さいprefixの数を考えれば良い。SAでWは順番に並んでおり、prefixはBWT中でlb(W)-1の位置以下にある。その数は、Occ(x, lb(W)-1)個。
  2. xから始まるxW以下のprefixはBWT中でub(W)の位置以下にある。その数はOcc(x,ub(W))個。0-origin indexingなので最後に-1する。

  • « 遺伝子機能学 第6回
  • 生体物質化学Ⅱ 第5回 »
hidden
Table of Contents
Published
May 30, 2019
Last Updated
May 30, 2019
Category
生物情報ソフトウエア論
Tags
  • 3S 95
  • 生物情報ソフトウエア論 4
Contact
Other contents
  • Home
  • Blog
  • Front-End
  • Kerasy
  • Python-Charmers
  • Translation-Gummy
    • 3S - Shuto's Notes
    • MIT
    • Powered by Pelican. Theme: Elegant