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

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

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

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

Q&A

解決済

3回答

1559閲覧

Ruby 2進数の1を10万回早く数える方法について

xekid

総合スコア15

Ruby

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

0グッド

0クリップ

投稿2017/05/30 11:40

######問題
入力にabがあるとして1からaの回数分2進数に変換して01で表示されているの物の中から1の数がb個の物だけを数えたいとします。
例.
a=10 b=2の場合、1の数が2個なのは
11, 101, 110, 1001, 1010なので
答えは5となる。
でこれがa=100000 b=17(答え:0回)の場合が知りたいのですが
######自分なりに考えた結果

lang

1a = 100000 2b = 17 3cnt = 0 4(1..a).each do |i| 5 ans = i.to_s(2).split(//) 6 ans = ans.map(&:to_i) 7 ans = ans.inject(:+) 8 if ans == b 9 cnt += 1 10 end 11end 12puts cnt

と自分なりに考えたりググってみたりでコーディングしたのですが、これだと処理時間がかかりすぎるといわれてしまいます。
######聞きたいこと
上記よりも早く処理ができる記述の仕方を教えて頂きたいです。
お手数ではありますがよろしくお願いします。

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

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

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

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

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

guest

回答3

0

ベストアンサー

ruby

1require 'benchmark' 2 3Benchmark.bm 0 do |r| 4 r.report do 5 a = 100000 6 b = 17 7 cnt = 0 8 (1..a).each do |i| 9 ans = i.to_s(2).split(//) 10 ans = ans.map(&:to_i) 11 ans = ans.inject(:+) 12 if ans == b 13 cnt += 1 14 end 15 end 16 puts cnt 17 end 18 r.report do 19 a = 100000 20 b = 17 21 puts (1..a).map{|i| i.to_s(2).scan(/1/).size == b }.select{|i|i}.size 22 end 23 r.report do 24 a = 100000 25 b = 17 26 puts (1..a).map{|i| i.to_s(2).scan(/1/).size}.select{|i|i == b}.size 27 end 28end 29

半分以下の時間で計算できそうです

投稿2017/05/30 12:16

NCC1701

総合スコア1680

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

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

xekid

2017/05/30 12:53

回答頂き本当にありがとうございます。 このコードは是非今後の参考にさせて頂きます。
guest

0

100000と17の場合は、コードに起こすまでもなく、暗算でできます。

二進法で表現して1が17個になる最小の数は1(2の0乗)+2(2の1乗)+…+65536(2の16乗)ですが、これは131071と、100000を超えてしまいます。つまり、100000未満には1つもありません。


(combinationで書いたコード)

ruby

1a = 1_000_000 2b = 17 3 4p (0..(a.bit_length - 1)).to_a.combination(b) 5 .map{ |arr| arr.map{ |i| 2**i }.reduce(&:+) } 6 .select{ |num| num <= a }.length

投稿2017/05/30 11:58

編集2017/05/30 12:28
maisumakun

総合スコア145183

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

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

xekid

2017/05/30 12:08

最速の回答有難うございます。 そもそもコーディングで回すのがナンセンスってことですね。 参考にさせて頂きます。 ちなみになんですが、もしRubyでコーディングするのなら上記の記述で問題はないでしょうか? 初心者なもので正解というか参考になる記述が知りたいのですが分かるようでしたら教えて頂けると有難いです。
maisumakun

2017/05/30 12:15

大きく分けて、2つの作戦があります。 ・分割統治法(「最上位のビットを立てるか立てないか」で分岐して、1桁範囲の狭い問題にしていく) ・Array#combinationで「何ビット目を立てるか」の組み合わせを生成しておいて、大きさチェックを行う
maisumakun

2017/05/30 12:39

試しに書いてみました(急いで書いたので、読みづらいかもしれません)。
xekid

2017/05/30 12:51

回答有難うございます。 貴重なご意見本当にありがとうございます。
guest

0

例えば
0b00000 ~ 0b11100 の範囲であれば、

0b0???? (0b00000 to 0b01111) 0b10??? (0b10000 to 0b10111) 0b110?? (0b11000 to 0b11011) 0b11100 (0b11100)

のように分割して考えられます。
0b0????のうち1が2つ含まれる組み合わせは combination(4,2) で計算すれば良いので
全部列挙するよりは早いと思います。

コードはちょっと汚いですが・・・。

Ruby

1a = 10000000 2b = 11 3 4def factorial(n) 5 return 1 if n == 0 6 return (1..n).inject(:*) 7end 8 9def permutation(n, r) 10 return 1 if n == 0 11 s = n - r + 1 12 return (s..n).inject(:*) 13end 14 15def combination(n, r) 16 r = [r, n - r].min 17 return n if r == 1 18 return 1 if r == 0 19 return permutation(n, r) / factorial(r) 20end 21 22num_left1 = 0 23ans = 0 24(a.bit_length-1).downto(0) { |pos| 25 if ( a[pos] == 1 ) then 26 length_rightside = pos 27 num_right1 = b - num_left1 28 if ( num_right1 >= 0 && length_rightside >= num_right1 ) then 29 ans += combination(length_rightside,num_right1) 30 end 31 num_left1 += 1 32 end 33} 34ans += 1 if num_left1 == b 35 36puts ans

たいていの入力に対して計算はすぐに終わると思います。

投稿2017/05/30 13:46

YsMana

総合スコア257

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問