tag:crieit.net,2005:https://crieit.net/tags/%E3%82%A2%E3%83%AB%E3%82%B4%E3%83%AA%E3%82%BA%E3%83%A0/feed 「アルゴリズム」の記事 - Crieit Crieitでタグ「アルゴリズム」に投稿された最近の記事 2021-07-14T15:39:56+09:00 https://crieit.net/tags/%E3%82%A2%E3%83%AB%E3%82%B4%E3%83%AA%E3%82%BA%E3%83%A0/feed tag:crieit.net,2005:PublicArticle/17515 2021-07-14T15:39:56+09:00 2021-07-14T15:39:56+09:00 https://crieit.net/posts/LAN-WPA-WPA2-WPA3 【ネットワーク】無線LANの認証機能(WPA・WPA2・WPA3)と暗号化アルゴリズム <p>ワイヤレスでの認証規格に、WPA、WPA2、 WPA3がある</p> <p>この記事書くのにヤクの毛大量に刈ってきましたが、まだ間違っているかもなので間違いみっけたらコメントくれると助かります</p> <h1 id="それぞれの規格一覧"><a href="#%E3%81%9D%E3%82%8C%E3%81%9E%E3%82%8C%E3%81%AE%E8%A6%8F%E6%A0%BC%E4%B8%80%E8%A6%A7">それぞれの規格一覧</a></h1> <p><a href="https://crieit.now.sh/upload_images/8433f144cd60ab84b23d7834aaa6f25960e95d8c41ab8.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/8433f144cd60ab84b23d7834aaa6f25960e95d8c41ab8.png?mw=700" alt="image" /></a></p> <h2 id="暗号化方式とは"><a href="#%E6%9A%97%E5%8F%B7%E5%8C%96%E6%96%B9%E5%BC%8F%E3%81%A8%E3%81%AF">暗号化方式とは</a></h2> <h5 id="TKIP"><a href="#TKIP">TKIP</a></h5> <p>RC4で暗号化し、Michaelで安全性を検証している</p> <h5 id="CCMP"><a href="#CCMP">CCMP</a></h5> <p>AESアルゴリズムをもとに暗号化をする方式</p> <h5 id="GCMP"><a href="#GCMP">GCMP</a></h5> <p>WPA3用。新種すぎてなかなか情報が得られず。(2017年にWPA2に脆弱性が見つかったらしいよ)</p> <h2 id="暗号化アルゴリズム(めちゃ数学です)"><a href="#%E6%9A%97%E5%8F%B7%E5%8C%96%E3%82%A2%E3%83%AB%E3%82%B4%E3%83%AA%E3%82%BA%E3%83%A0%EF%BC%88%E3%82%81%E3%81%A1%E3%82%83%E6%95%B0%E5%AD%A6%E3%81%A7%E3%81%99%EF%BC%89">暗号化アルゴリズム(めちゃ数学です)</a></h2> <h5 id="RC4"><a href="#RC4">RC4</a></h5> <p>共通鍵暗号方式で、WEPやWPA、SSL/TLSなどに使われている</p> <p>データを1ビットずつ暗号化していく<strong>ストリーム暗号</strong>で、<br /> 疑似乱数を生成して、平文との排他的論理和を計算し、それを暗号文としている。</p> <p>暗号化した時と同じ疑似乱数を使ってもう一度排他的論理和を計算すると、平文に戻るという仕組みです。<br /> 擬似乱数を共通鍵として持っておけば、複合化できます。</p> <p>排他的論理和を2回繰り返すと元に戻るという数学チックなやり方を使っていますね</p> <h5 id="AES"><a href="#AES">AES</a></h5> <p>無線LANやファイルの暗号化で使われている。</p> <p><strong>Advanced Encryption Standard</strong>の略<br /> RC4とは対照的に、一定量のデータをまとめて暗号化する<strong>ブロック暗号</strong>を利用しています。<br /> AESで使う4 種類の変換は、「SubBytes」「ShiftRows」「MixColumns」「AddRoundKey」<br /> これを基本的にこの順番で、何回か繰り返して作られています。</p> <h6 id="SubBytes"><a href="#SubBytes">SubBytes</a></h6> <ol> <li><p>8ビットの情報をガロア体GF(28)の逆数に変換<br /> 最初の4ビットがX, 後半4ビットがYになります。<br /> <a href="https://crieit.now.sh/upload_images/fef47aea7e6009d8c040e4b1a1a121e460eada947f9c1.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/fef47aea7e6009d8c040e4b1a1a121e460eada947f9c1.png?mw=700" alt="スクリーンショット 2021-07-11 20.48.28.png" /></a><br /> GF(28)の逆数変換テーブル</p></li> <li><p>その結果をもとに、行列変換をします。</p></li> </ol> <p><a href="https://crieit.now.sh/upload_images/dc0b31bc7bf1241dd6b3cc7e671dda9b60eadc34a6a16.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/dc0b31bc7bf1241dd6b3cc7e671dda9b60eadc34a6a16.png?mw=700" alt="スクリーンショット 2021-07-11 20.55.28.png" /></a></p> <p>a0からa7までに、逆数変換した後の値を入れて、行列変換します。</p> <h6 id="ShiftRows"><a href="#ShiftRows">ShiftRows</a></h6> <p>その名の通り、列を並べ替えていきます。</p> <p>4×4の行列を、行ごとで一つずつシフト</p> <p><a href="https://crieit.now.sh/upload_images/2da2a1210ecce69718ddeb021008d2e260eae10711b1d.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/2da2a1210ecce69718ddeb021008d2e260eae10711b1d.png?mw=700" alt="スクリーンショット 2021-07-11 21.16.03.png" /></a></p> <h6 id="MixColums"><a href="#MixColums">MixColums</a></h6> <p>行列を、列ごとに掛け算していきます。<br /> 行列は、<a href="https://crieit.now.sh/upload_images/8396bd9aad7141bdd503b24f158fb1db60eae2a11ad9c.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/8396bd9aad7141bdd503b24f158fb1db60eae2a11ad9c.png?mw=700" alt="スクリーンショット 2021-07-11 21.22.53.png" /></a><br /> で求められます</p> <h6 id="AddRoundKey"><a href="#AddRoundKey">AddRoundKey</a></h6> <p>Key Scheduleから作成された行列との排他的論理和をとります。至って単純。</p> <p>アルゴリズムの参考サイト<br /> <a target="_blank" rel="nofollow noopener" href="http://www.ihpc.se.ritsumei.ac.jp/hpc/papers/b05/umehara.pdf">http://www.ihpc.se.ritsumei.ac.jp/hpc/papers/b05/umehara.pdf</a><br /> <a target="_blank" rel="nofollow noopener" href="https://www.cqpub.co.jp/DWM/contest/2004/specification2004.pdf">https://www.cqpub.co.jp/DWM/contest/2004/specification2004.pdf</a><br /> その他諸々</p> <h2 id="認証方式について"><a href="#%E8%AA%8D%E8%A8%BC%E6%96%B9%E5%BC%8F%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6">認証方式について</a></h2> <h4 id="WPAパーソナル"><a href="#WPA%E3%83%91%E3%83%BC%E3%82%BD%E3%83%8A%E3%83%AB">WPAパーソナル</a></h4> <p>認証サーバを使わないモード</p> <h5 id="PSK"><a href="#PSK">PSK</a></h5> <p>Pre-Shared Keyの略。意味は、「共有パスワードキー」<br /> まー従来使われていたやつ。</p> <h5 id="SAE"><a href="#SAE">SAE</a></h5> <p>Simultaneous Authentication of Equalsの略。<br /> クライアント役とサーバ役が時と場合によりコロコロ入れ替わる。<br /> 認証パスワードが予測できない。<br /> 鍵交換に4ウェイハンドシェイクするらしい(4ウェイって初めて聞いた笑)</p> <h4 id="WPAエンタープライズ"><a href="#WPA%E3%82%A8%E3%83%B3%E3%82%BF%E3%83%BC%E3%83%97%E3%83%A9%E3%82%A4%E3%82%BA">WPAエンタープライズ</a></h4> <p>認証サーバを使用するモード。<br /> 企業で使われる</p> <h5 id="IEEE 802.1X/EAP"><a href="#IEEE+802.1X%2FEAP">IEEE 802.1X/EAP</a></h5> <p>WEPにはない認証方式を作るためにできた。<br /> サプリカント、認証装置、認証サーバを用いる。<a href="https://crieit.now.sh/upload_images/4f2fb798ce8ede0f416577ce9af4f98660eaf318e1571.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/4f2fb798ce8ede0f416577ce9af4f98660eaf318e1571.png?mw=700" alt="スクリーンショット 2021-07-11 22.33.08.png" /></a></p> <p>認証方式に、MD5とか。TLSとか。よく聞いたとこあるようなやつが使われている。</p> skyms tag:crieit.net,2005:PublicArticle/16685 2021-02-14T18:24:50+09:00 2021-02-14T18:50:36+09:00 https://crieit.net/posts/Ruby-SA-IS-suffix-array-induced-sorting Rubyで素朴なSA-IS(suffix array induced sorting)を書いてみた <p><a target="_blank" rel="nofollow noopener" href="https://memo88.hatenablog.com/entry/20160303/1457007566">FM-index</a> と同様に、ほぼ最適化をしていない素朴な実装を作ってみました。</p> <p>元の論文の実装は空間効率まで考慮して書かれていてすごい! ……のですが、まずはそれ以外の部分の仕組みを理解したかったので、以下のコードではそこらへんも無視しています。</p> <p>(※ <a target="_blank" rel="nofollow noopener" href="https://memo88.hatenablog.com/entry/20160317/1458224137">2016-03-17 に書いた記事</a>のクロスポストです)</p> <h1 id="コード"><a href="#%E3%82%B3%E3%83%BC%E3%83%89">コード</a></h1> <pre><code class="ruby">require "minitest/autorun" SENTINEL = "$" TYPE_L = "L" TYPE_S = "S" NAME_CHARS = [ "a","b","c","d","e","f","g","h","i","j", "k","l","m","n","o","p","q","r","s","t", "u","v","w","x","y","z" ] def _assert(exp) raise "must not happen" if exp == false end # from &lt;= i &lt;= to def _each_up(from, to) ( from.upto(to) ).each {|i| yield(i) } end # from &gt;= i &gt;= to def _each_down(from, to) ( from.downto(to) ).each {|i| yield(i) } end class Buckets def initialize(s_cs) @ary = [] @freq_map = make_freq_map(s_cs) @sorted_uniq_chars = @freq_map.keys().sort() clear(s_cs.length()) end def clear(length) _each_up(0, length - 1) {|i| @ary[i] = nil } end def make_freq_map(s_cs) uniq_cs = s_cs.uniq() # 初期化 freq_map = {} uniq_cs.each {|c| freq_map[c] = 0 } s_cs.each {|c| freq_map[c] += 1 } return freq_map end def first_pos(c) i = 0 found = false @sorted_uniq_chars.each {|iter_c| if (iter_c == c) found = true break end i += num_bucket_elements(iter_c) } return i end def num_bucket_elements(c) return @freq_map[c] end def add_l(c, si) # バケツの先頭・末尾 bia = first_pos(c) biz = bia + num_bucket_elements(c) - 1 i = nil # 線形走査。遅い。 _each_up(bia, biz) {|bi| if @ary[bi] != nil next end i = bi break } _assert( i != nil ) @ary[i] = si end def add_s(c, si) # バケツの先頭・末尾 bia = first_pos(c) biz = bia + num_bucket_elements(c) - 1 i = nil # 線形走査。遅い。 _each_down(biz, bia) {|bi| if @ary[bi] != nil next end i = bi break } _assert( i != nil ) @ary[i] = si end def num_chars() return @ary.length() end def get(i) return @ary[i] end def set(i, value) @ary[i] = value end def to_array() return @ary end end def make_types(s_cs) types = [] types[s_cs.length() - 1] = TYPE_S # sentinel は S prev_type = TYPE_S prev_c = SENTINEL _each_down(s_cs.length() - 2, 0) {|si| c = s_cs[si] if (c < prev_c) type = TYPE_S elsif (c > prev_c) type = TYPE_L else type = prev_type end types[si] = type prev_c = c prev_type = type } return types end def extract_lms_positions(types) sis = [] prev_type = TYPE_S # sentinel _each_down(types.length() - 2, 0) {|si| type = types[si] if (type == TYPE_L && prev_type == TYPE_S) sis.unshift(si + 1) end prev_type = type } return sis end def add_lms_to_bkts(bkts, s_cs, lms_sis) lms_sis.each {|lms_si| c = s_cs[lms_si] bkts.add_s(c, lms_si) } return bkts end def induce_l(bkts, s_cs, types) # 上から _each_up(0, bkts.num_chars() - 1) {|bi| si = bkts.get(bi) next if si == nil next if si == 0 next if types[si - 1] != TYPE_L prev_c = s_cs[si - 1] bkts.add_l(prev_c, si - 1) } return bkts end def induce_s(bkts, s_cs, types) # 下から _each_down(bkts.num_chars() - 1, 0) {|bi| si = bkts.get(bi) next if si == nil next if si == 0 next if types[si - 1] != TYPE_S prev_c = s_cs[si - 1] bkts.add_s(prev_c, si - 1) } return bkts end def is_lms(lms_sis, si) return lms_sis.include?(si) end def induced_sort(bkts, s_cs, types, lms_sis) bkts = induce_l(bkts, s_cs, types) # sentinel 以外の LMS を除去 _each_up(1, bkts.num_chars() - 1) {|bi| si = bkts.get(bi) bkts.set(bi, nil) if is_lms(lms_sis, si) } bkts = induce_s(bkts, s_cs, types) return bkts end def is_same_substring(s_cs, ai, bi, lms_sis) i = 0 is_same = true while true if s_cs[ai + i] != s_cs[bi + i] is_same = false break end if i >= 2 # 元論文ではタイプ(L/S)による判別も行なっている a_is_lms = is_lms(lms_sis, ai + i) b_is_lms = is_lms(lms_sis, bi + i) if (a_is_lms && b_is_lms) break elsif (! a_is_lms && b_is_lms) is_same = false break elsif (a_is_lms && ! b_is_lms) is_same = false break else # both are not LMS end end i += 1 _assert(i < s_cs.length()) end return is_same end def get_name(i) name = NAME_CHARS[i] if (name == nil) raise "names (LMS-substring) is too many" end return name end def to_names(s_cs, lms_sis, sorted_lms_sis_temp) is_unique = true # name index ni = 0 names = [] # 1個目 names.unshift(get_name(ni)) ni += 1 # 2個目以降 _each_up(0, sorted_lms_sis_temp.length() - 2) {|ai| sia = sorted_lms_sis_temp[ai] sib = sorted_lms_sis_temp[ai + 1] if (is_same_substring(s_cs, sia, sib, lms_sis)) is_unique = false else ni += 1 end names.unshift(get_name(ni)) } return [names, is_unique] end def sa_is(s_cs) bkts = Buckets.new(s_cs) types = make_types(s_cs) lms_sis = extract_lms_positions(types) # -------------------------------- # induced sort 1回目 # LMS-substring をソートするのが目的 bkts = add_lms_to_bkts(bkts, s_cs, lms_sis) bkts = induced_sort(bkts, s_cs, types, lms_sis) # この時点で LMS-substring がソートされた状態になる # (ただし、重複した LMS-substring 同士の順序は未確定) # -------------------------------- # LMS-substring のソート # LMS だけを抜き出す sorted_lms_sis_temp = [] _each_up(0, bkts.num_chars() - 1) {|bi| si = bkts.get(bi) if (is_lms(lms_sis, si)) sorted_lms_sis_temp << si end } names, is_unique = to_names(s_cs, lms_sis, sorted_lms_sis_temp) sorted_lms_sis = nil if (is_unique) sorted_lms_sis = sorted_lms_sis_temp else ret = sa_is(names) sorted_lms_sis = [] ret.each {|i| sorted_lms_sis.unshift(lms_sis[i]) } end # -------------------------------- # induced sort 2回目 # 1回目のソートは LMS-substring のソート結果を得るのが目的だったので # 一旦空にして良い。 bkts.clear(s_cs.length()) bkts = add_lms_to_bkts(bkts, s_cs, sorted_lms_sis) bkts = induced_sort(bkts, s_cs, types, sorted_lms_sis) return bkts.to_array() end class SaIsTest < Minitest::Test def test_make_lms_sis_1 # 0 1 assert_equal([1], extract_lms_positions(["L","S"])) end def test_make_lms_sis_2 # 0 1 2 assert_equal([2], extract_lms_positions(["S","L","S"])) end def test_make_lms_sis_3 # 0 1 2 3 assert_equal [2], extract_lms_positions(["S","L","S","S"]) end def test_make_lms_sis_4 # 0 1 2 3 4 5 assert_equal([2, 5], extract_lms_positions(["S","L","S","L","L","S"])) end def test_is_same_substring_1 # 0 1 2 3 4 5 6 7 # L S L S L S L S t_cs = ["b","a","b","a","b","a","b","$"] lms_sis = [1,3,5,7] assert_equal true, is_same_substring(t_cs, 1, 3, lms_sis) end def test_is_same_substring_2 # 0 1 2 3 4 5 6 7 # L S L S L S L S s_cs = ["b","a","c","a","b","a","b","$"] lms_sis = [1,3,5,7] assert_equal false, is_same_substring(s_cs, 1, 3, lms_sis) end def test_sa_is_bbaaddaaddaaccaa # 0 1 # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 # L L S S L L S S L L S S L L L L S s_cs = ["b","b","a","a","d","d","a","a","d","d","a","a","c","c","a","a", SENTINEL] assert_equal( [16 ,15 ,14 ,10 ,6 ,2 ,11 ,7 ,3 ,1 ,0 ,13 ,12 ,9 ,5 ,8 ,4], sa_is(s_cs) ) end def test_sa_is_eaefaegaefaag # 0 1 # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 # L S S L S S L S S L S S L S s_cs = ["e","a","e","f","a","e","g","a","e","f","a","a","g", SENTINEL] assert_equal( [13,10,7,1,4,11,0,8,2,5,9,3,12,6], sa_is(s_cs) ) end def test_sa_is_mississippi # 0 1 # 0 1 2 3 4 5 6 7 8 9 0 1 # L S L L S L L S L L L S s_cs = ["m","i","s","s","i","s","s","i","p","p","i", SENTINEL] assert_equal( [11,10,7,4,1,0,9,8,6,3,5,2], sa_is(s_cs) ) end def test_sa_is_abracadabra # 0 1 # 0 1 2 3 4 5 6 7 8 9 0 1 # S S L S L S L S S L L S s_cs = ["a","b","r","a","c","a","d","a","b","r","a", SENTINEL] assert_equal( [11,10,7,0,3,5,8,1,4,6,9,2], sa_is(s_cs) ) end end </code></pre> <p>(追記 2021-02-14) Ruby 3.0.0 向けに minitest まわりを微修正しました。</p> <h1 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h1> <ul> <li>2018-01-30 <a target="_blank" rel="nofollow noopener" href="https://mametter.hatenablog.com/entry/20180130/p1">SA-IS 法のメモ - まめめも</a> <ul> <li>Ruby による実装</li> </ul></li> </ul> sonota486 tag:crieit.net,2005:PublicArticle/16678 2021-02-08T18:51:49+09:00 2021-02-08T18:55:50+09:00 https://crieit.net/posts/Ruby-FM-index Rubyで素朴なFM-indexを書いてみた <p>BWT、検索処理の最適化・高速化は行なっていません(SA-IS、ウェーブレット行列などは使っていません)。 BWT から検索まで全体の流れが見渡せる最小限の実装にしました。 せっかくなので Ruby に馴染みのない方が見ても読みやすいと思われる書き方にしています(returnやメソッド呼び出しの括弧を省略しない、など)。</p> <p>(※ <a target="_blank" rel="nofollow noopener" href="https://memo88.hatenablog.com/entry/20160303/1457007566">2016-03-03 に書いた記事</a>のクロスポストです)</p> <h1 id="参考にしたもの"><a href="#%E5%8F%82%E8%80%83%E3%81%AB%E3%81%97%E3%81%9F%E3%82%82%E3%81%AE">参考にしたもの</a></h1> <ul> <li><a target="_blank" rel="nofollow noopener" href="http://www.langmead-lab.org/teaching-materials/">Teaching Materials</a> - ここに置いてある「Burrows-Wheeler Transform and FM Index」というタイトルの PDF(ジョンズ・ホプキンス大学 Ben Langmead さんの講義資料)</li> </ul> <p>最初の取っ掛かりとして分りやすかったのがこれ。要点を押さえた簡潔な図と Python コードを眺めてるだけでだいぶ分かった気になれます。</p> <hr /> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/erukiti/cerebrums">erukiti/cerebrums: 文章・情報共有ソフト</a></li> </ul> <p>もっと具体的なところについては実装を見た方が早いということで <a target="_blank" rel="nofollow noopener" href="http://qiita.com/erukiti/items/f11f448d3f4d73fbc1f9">ハクビシンにもわかる全文検索</a> の erukiti さんの実装を参考にさせてもらいました。CoffeeScript 製。</p> <h1 id="コード"><a href="#%E3%82%B3%E3%83%BC%E3%83%89">コード</a></h1> <pre><code>- range - (a..b) => [a, b] b is included - (a...b) => [a, b) b is not included - a.downto(b) => [a, a-1, ... b+1, b] - method { |x| ... } => in JavaScript: method((x)=>{ ... }) </code></pre> <pre><code class="ruby">require "minitest/autorun" SENTINEL = "$" def to_bwm(t) tt = t + SENTINEL + t rows = (0..t.length).map { |i| tt[i..(i + t.length)] } return rows.sort end # Burrows-Wheeler transform def bwt(t) bwm = to_bwm(t) return bwm.map { |cs| cs[-1] }.join("") end def rank_less_than(cs, c) return cs.count { |_c| _c < c } end def rank(cs, c, i) return (0...i).count { |j| cs[j] == c } end # LF mapping def map_lf(cs, c, i) return rank_less_than(cs, c) + rank(cs, c, i) end # String before c def backward_chars(bwt_t, c, s, e) bwt_cs = bwt_t.split("") # to array of chars cb = c # T(i) ca = nil # T(i-1) result = "" while true s = map_lf(bwt_cs, cb, s) e = map_lf(bwt_cs, cb, e) # assert e - s == 1 ca = bwt_t[s] if (ca == SENTINEL) break end result += ca cb = ca end return result.reverse end def reverse(bwt_t) # sentinel は F列では必ず 0 行目のみに存在する # sentinel is always at F[0] s = 0 # start e = 1 # end return backward_chars(bwt_t, SENTINEL, s, e) end def search_internal(bwt_t, q) bwt_cs = bwt_t.split("") # to array of chars s = 0 e = bwt_t.length (q.length - 1).downto(0).each { |i| s = map_lf(bwt_cs, q[i], s) e = map_lf(bwt_cs, q[i], e) if (s >= e) return nil end } return [s, e] end def search(bwt_t, q) s, e = search_internal(bwt_t, q) return s == nil ? 0 : (e - s) end class FmIndexTest < Minitest::Test def setup t = "abaaba" @bwt_t = bwt(t) end def test_bwt assert_equal("abba$aa", @bwt_t) end # 1文字の検索 def test_one_char s, e = search_internal(@bwt_t, "a") assert_equal(1, s) assert_equal(5, e) # 出現回数 num_hits = search(@bwt_t, "a") assert_equal(4, num_hits) end # 2回出現する def test_2_hits s, e = search_internal(@bwt_t, "aba") assert_equal(3, s) assert_equal(5, e) assert_equal(2, search(@bwt_t, "aba")) end # 1回出現する def test_one_hit s, e = search_internal(@bwt_t, "aaba") assert_equal(2, s) assert_equal(3, e) assert_equal(1, search(@bwt_t, "aaba")) end # 存在しない組み合わせ def non_existent_combination s, e = search_internal(@bwt_t, "baba") assert_equal(nil, s) assert_equal(nil, e) assert_equal(0, search(@bwt_t, "baba")) end # 存在しない文字 def non_existent_char s, e = search_internal(@bwt_t, "x") assert_equal(nil, s) assert_equal(nil, e) assert_equal(0, search(@bwt_t, "x")) end def test_reverse bwt_t = bwt("mississippi") assert_equal("ipssm$pissii", bwt_t) assert_equal("mississippi", reverse(bwt_t)) end end </code></pre> <ul> <li>(追記 2021-02-08) Ruby 3.0.0 向けに minitest まわりを微修正しました。</li> </ul> sonota486 tag:crieit.net,2005:PublicArticle/16451 2020-12-31T12:00:55+09:00 2020-12-31T12:03:07+09:00 https://crieit.net/posts/UMAP UMAPに関して書いてみました <p>拙文ながら、高次元データ可視化の手法、UMAPとt-SNEに関して書いてみたので、紹介。</p> <ul> <li><p>UMAPの仕組み ─ 低次元化の理屈を理解してみる<br /> <a target="_blank" rel="nofollow noopener" href="https://kntty.hateblo.jp/entry/2020/12/14/070022">https://kntty.hateblo.jp/entry/2020/12/14/070022</a></p></li> <li><p>t-SNEやUMAPで、裾の広い分布を使う理由(混雑問題)<br /> <a target="_blank" rel="nofollow noopener" href="https://kntty.hateblo.jp/entry/2020/12/31/092334">https://kntty.hateblo.jp/entry/2020/12/31/092334</a></p></li> </ul> knttylap tag:crieit.net,2005:PublicArticle/15188 2019-07-02T13:00:13+09:00 2019-07-03T11:45:10+09:00 https://crieit.net/posts/c81cf757cf261b0453084bb2451b8a6f ビジネスデータ分析を支える技術 <p>ビジネスデータ分析を支える技術について簡単に解説したいと思います。データ分析は割と広い世界になっていて、使われる技術は色々とあります。まず下記の図を見てください。</p> <p><a href="https://crieit.now.sh/upload_images/d49c72d087511c590d1aeeccc7f5c4895d1acdf9f3a6b.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/d49c72d087511c590d1aeeccc7f5c4895d1acdf9f3a6b.jpg?mw=700" alt="Data Analysis Process.jpg" /></a></p> <p>データ分析システムをフルに作ろうとすると、だいたいこの図のようなフローになると思います。左から右へ流れて行きます。まず、一番左のグループです。データが生成される場所ですね。<br /> データは形式により大きく3つに分かれます。</p> <pre><code> 構造化データ 非構造化データ 半構造化データ </code></pre> <p>ここで必要な技術は何でしょう? データ理解です。これが実は案外難しい。習うより慣れろではないですが、経験がものを言うエリアです。分析をするプロセスに持ち込むデータの量、形式、質などを期待されるアウトプットを考えて、整理し、必要なら加工していかなけばならないのですね。欠損が無いか、もしあればどう補うのか。センサーデータなどのの場合は、採用するかどうかの基準値を決めなければなりません。非構造化データの場合も、どのデータを使うのかの基準値が必要になります。分析に使用するアルゴリズムによっては、必要とするデータ量が決まっているものもあります。後工程のことを考えつつ、データを理解し、必要なアクションを取って行くスキルが求められます。ここを安易に通過すると、期待する分析結果が得られないリスクがあります。</p> <p>次に、データをためるDWH(データウエアハウス)とData Lake(データレイク)。構造化データ(典型的な構造化データは、RDBMS)はDWHにため込むことになります。ほとんどがCloud DWHになります。半構造化データと非構造化データは、Data Lakeに保存しておきます。従って、DWHとData Lakeの技術スキルが必要とされます。</p> <p>次が、分析プロセス。データが保存されているDWHやData Lakeから分析に必要なデータを抽出して持っていく必要があるわけです。Data Martを作る場合もあります。レガシーシステムですとETLの知識が必要でしたがCloudでは、もう少し簡単になっています。DWHから分析に必要なデータを抽出する為には、SQLがわかっていなければなりません。Cloud Data Lakeの場合は、データカタログという機能が用意されており、抽出が容易になっています。</p> <p>分析プロセスについては、下図を見て下さい。</p> <p><a href="https://crieit.now.sh/upload_images/6b64021e020be575d571e445986395495d1ad53473b69.jpg" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/6b64021e020be575d571e445986395495d1ad53473b69.jpg?mw=700" alt="datanalysisprocessfigure.jpg" /></a></p> <p>分析プロセスで必要なスキルは、どのようなものでしょうか?<br /> ここでは、データは使える状態になっているので、分析に使うアルゴリズムを決めます。データと期待されているアウトプットを考えて、アルゴリズムを決めるスキルが必要です。</p> <p>処理プロセスとしては最後になる、データビジュアライゼーションです。具体的には、Microsoft Power BIやTableauといったセルフサービスBIのスキルですね。最近は、第二世代も出てきていますので、この分野の知識とスキルも大切です。</p> <p>ビジュアライゼーションされた分析結果を読み取り、意思決定を支援できる知見を得ることがビジネスデータ分析の目的です。この知見発見というのが、ビジネスデータ分析の最大の難所です。ドメイン知識も必要ですし、おのれの知識や経験を総動員して考えなければなりません。クライアントやビジネスパートナーと知見発見ミーティングを持つ必要もありますが、それでもたたき台はコンサルタントが作成する必要があります。頑張りどころですし、ここで評価も決まってきます。</p> Masao Kato