質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.49%
Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

Q&A

解決済

3回答

1467閲覧

Ruby selectの条件文らしきところが分からない

_Victorique__

総合スコア1392

Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

0グッド

0クリップ

投稿2017/06/09 15:05

###前提・実現したいこと
最大クリーク問題に関するコードで分からない部分があります。

###該当のソースコード

Ruby

1(1...2**n).each do |i| 2 grp = n.times.select do |j| 3 i >> j & 1 > 0 4 end 5 if (grp.combination(2).to_a - rels).empty? 6 ans = [ans, grp.size].max 7 end 8end

###試したこと
このコードは全通りのリストを作って、条件に合うものをしらみつぶしに調べるものです。relsは条件のリスト。

Ruby

1grp = n.times.select do |j| 2 i >> j & 1 > 0 #ここ 3 end

この部分でおそらくパターンを生成しているのですが、このコードの解釈の仕方が分かりません。これはbit演算だと思うのですが、どうしてこのようなコードで全パターン生成できるのでしょうか?

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

ベストアンサー

  • i >> jij個右シフトします。
  • x & 1x1の論理積ですが、10b1(1bit目のみ1)なので、xの1bit目が1なら1、0なら0になります。つまり、i >> j & 1は「ij個右シフトしたものの1bit目が1なら1、0なら0」となりますが、j個右シフトしたあとの1bit目は、もともとの数値のj+1bit目になりますので、これは「ij+1bit目が1なら1、0なら0」という
  • x > 00より大きいかどうかです。i >> j & 1 >0は「ij+1bit目が1なら1、0なら0、となる数値が0より大きいかどうか」となり、「ij+1bit目が1なら真、0なら偽」となります。

つまり、i >> j & 1 >0は数値ij番目のbit(0番目から始まることに注意)が立っているかどうかです。bitが立っている場合のみselectで選択しているので、

Ruby

1grp = n.times.select do |j| 2 i >> j & 1 > 0 3end

での、grpは数値iで立っているbitの番号の配列になります。簡単に言うとgrp.map{|x|2**x}.sum == iが成り立つとも言えます。(Array#sumは2.4から追加されたメソッドなので、2.3以下はinject(:+)と書いてください)

なお、i1...2**nを列挙している物でした。2のn乗以下が保証されているため、n番目以上のbitが立つことはありません。なのでn.timesでは0からn-1まで(nは含まない)の列挙ですが、全てのbitを網羅することができます。

1...2**nは、「0からn-1までの数値の一つ以上取り出したときの組合せ」について、それぞれを○番目のbitとしたときに、全ての数値を網羅していることになります。ですから、全体として、「0からn-1までの数値の一つ以上取り出したときの組合せ」全てを網羅していると言うことになります。n個の頂点があるグラフにおける、頂点の組合せ全てと言えます。

あとは、grp.combination(2).to_aで、その組合せで存在する可能性がある全ての辺の集合をつくって、それが、全て存在するかを確認しているかどうかという風になっています。

アルゴリズム的には最大クリーク問題がNP困難なので全てを回す以外ないと思いますが、大きい方から求めた方が期待される平均時間は短くなるような気がします(最悪時間の計算量は変わりませんが)。

Ruby

1require 'set' 2def check(n, rels) 3 rels = rels.to_set 4 list = n.times.to_a 5 (2..n).reverse_each do |i| 6 list.combination(i) do |grp| 7 return i if grp.combination(2).to_set.subset?(rels) 8 end 9 end 10 return 11end

大きい方から求めて、あっていれば早期returnとしています。二重のdoブロックをbrekaで抜けるのが面倒ですので、メソッドにしたほうが楽です。Setを使うと速くなるかどうかは微妙な所です(ハッシュ値生成の方が高コストになる可能性もあるので)。

投稿2017/06/09 21:05

raccy

総合スコア21735

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

i >> j & 1 > 0 の式を忠実にたどれば、

演算子の優先順位は、

(右ビットシフト)

&(ビットでの論理積)

(比較演算子)

の順番ですから、

((i>>j) & 1) > 0

の順で計算されます。
したがって、
i >> j で0からn-1まで繰り返し右シフトがおきます。
それぞれの回の右端ビットが判定対象です。

& 1 で、右端ビット以外は0で埋められ、右端ビットが1ならば1を、
0ならば0を返します。つまり、2進数表現でのある桁が1かどうかを
判定します。

> 0で、その右端ビットまでせり出された桁が1ならばtrueを、
0ならばfalseを返し、その繰り返しでgrpの配列がたまっていきます。
例えば、十進数の13、二進数の1101 ならば、{true,false,true,true}の配列が
作られます。
あとは、配列grpからconbinationメソッドで全パターンの組合せを生成し、
先に与えられている配列relsの要素にマッチしたものがあるかどうかで、
マッチする最大個数を残しています。

これが、2のn乗回繰り返され、最後に残ったmaxが、1から2のn乗までの整数の
内でパターンにあう最大個数になります。

投稿2017/06/09 19:42

編集2017/06/09 19:48
seastar3

総合スコア2285

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

私はこのコードが全く分かりませんが

Ruby

1grp = n.times.select do |j| 2 i >> j & 1 > 0 #ここ 3end

この部分は
0からn-1までの連番配列のうち
jだけ右へシフトした結果が奇数に
なっているものを取り出している
みたいな処理だと私は予想します。

シフト演算子
5-4.ビット演算子

このようにして作った配列から
2個を選んだ時の全組み合わせの配列
を作り、それがrelsと同じ配列だった場合に
ansにansとgrp.sizeを比較して大きい方を
採用しているという感じではないでしょうか。

combination (Array)

付け焼刃で答えているので
間違えている可能性が非常に高いです。

投稿2017/06/09 19:05

編集2017/06/09 21:55
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.49%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問