Kombinacja bez powtórzeń

Losowanie szóstki “Chybił trafił” (albo jak kto woli “Chybił nie trafił”)

temp = Array 1..49 tab = Array.new 6.times {tab << temp.delete_at(rand(temp.length))} puts tab
Czy ktoś ma lepszy pomysł ??

Taki sobie oneliner.

(1..49).sort{rand()-0.5}.first(6)

Teraz robię taką symulację. Przeprowadzam losowanie dopóki nie trafię piątki (szóstki nawet nie próbuję bo się nie doczekam)

[code]def losuj2
(1…49).sort{rand()-0.5}.first(6)
end

def losuj
temp = Array 1…49
tab = Array.new
6.times {tab << temp.delete_at(rand(temp.length))}
tab
end

i=0
begin
i+=1
t1 = losuj
t2 = losuj
end while (t1 & t2).length != 5

puts “Trafiono za #{i} razem”[/code]
funkcja losuj2 działa z oczywistych powodów wolniej.

Może ktoś teraz znajdzie bardziej optymalny sposób na tą symulację.

[quote=Dulo]Taki sobie oneliner.

(1..49).sort{rand()-0.5}.first(6)

[/quote]

Akapit “Just one more thing”

[quote=RORMaster]Losowanie szóstki “Chybił trafił” (albo jak kto woli “Chybił nie trafił”)

temp = Array 1..49 tab = Array.new 6.times {tab << temp.delete_at(rand(temp.length))} puts tab
Czy ktoś ma lepszy pomysł ??[/quote]
Jeśli kluczowa nie jest dla Ciebie mega wydajność to od ruby 1.8.7 wzwyż możesz:

(1..49).to_a.shuffle.first(6)

Twój sposób, ubrany w odpowiednią metodę, jak dla mnie jest bardzo dobry, z małą poprawką (bardziej idiomatyczny ruby):

arr = (1..49).to_a puts 6.times.map { arr.delete_at(rand(arr.length)) }

[code]require ‘active_support/all’

a = (1…49).to_a
my_numbers = a.sample(6)

i=0
begin
i+=1
lotto = a.sample(6)
common = my_numbers & lotto
end while common.size < 5 # common.size != 5

puts “Trafiono za #{i} razem”[/code]
I czy warto użyć Set ?:

[code]require ‘active_support/all’
require ‘set’

Benchmark.bm() do |x|
x.report(“set”) do

a = (1..49).to_a
my_numbers = a.sample(6).to_set

100_000.times do
  lotto = a.sample(6).to_set
  common = my_numbers & lotto
  common.size
end

end

x.report(“ary”) do
a = (1…49).to_a
my_numbers = a.sample(6)

100_000.times do
  lotto = a.sample(6)
  common = my_numbers & lotto
  common.size
end

end
end

  user     system      total        real

set 2.180000 0.010000 2.190000 ( 2.236029)
ary 0.440000 0.000000 0.440000 ( 0.453888)[/code]

Kod `sample’ można sobie obejrzeć pod :
$GEM_HOME/gems/activesupport-3.0.0/lib/active_support/core_ext/array/random_access.rb

random_access.rb

... n.times do |i| r = i + Kernel.rand(size - i) result[i], result[r] = result[r], result[i] end ...
Już bardziej optymalnie chyba nie będzie

[quote=paneq]I czy warto użyć Set ?:

[code]require ‘active_support/all’
require ‘set’

Benchmark.bm() do |x|
x.report(“set”) do

a = (1..49).to_a
my_numbers = a.sample(6).to_set

100_000.times do
  lotto = a.sample(6).to_set
  common = my_numbers & lotto
  common.size
end

end

x.report(“ary”) do
a = (1…49).to_a
my_numbers = a.sample(6)

100_000.times do
  lotto = a.sample(6)
  common = my_numbers & lotto
  common.size
end

end
end

  user     system      total        real

set 2.180000 0.010000 2.190000 ( 2.236029)
ary 0.440000 0.000000 0.440000 ( 0.453888)[/code]
[/quote]
Bardzo zdziwił mnie ten wynik swego czasu, ale stwierdziłem, że darowanemu benchmarkowi nie zagląda się w zęby :slight_smile:
Natomiast postanowiłem to sprawdzić przy innej okazji, i wyszło na jaw, że set (zgodnie z intuicją) działa bardzo szybko dla & oraz |. Problem z wydajnością leży gdzie indziej - w zamianie tablicy na set.

[code=ruby]require ‘set’
require ‘benchmark’

SIZE = 100_000_000
a = []
SIZE.times{|i| a << rand(SIZE/1000)}
a1 = a[0…SIZE/2]
a2 = a[SIZE/2…-1]
s1 = Set.new(a1)

this forces conversion from array to set

s1.class
s2 = Set.new(a2)
s2.class

Benchmark.bm() do |x|
x.report(“set &”) do
r = s1 & s2
r.size
end
x.report(“set |”) do
r = s1 | s2
r.size
end

x.report(“array &”) do
r = a1 & a2
r.size
end
x.report(“array |”) do
r = a1 | a2
r.size
end
end[/code]

user system total real set & 0.160000 0.000000 0.160000 ( 0.162904) set | 0.110000 0.000000 0.110000 ( 0.116595) array & 14.620000 0.010000 14.630000 ( 14.638718) array | 28.630000 0.010000 28.640000 ( 28.656151)
Jak się robi benchmark, to warto wiedzieć co się benchmarkuje 8)

Jesteś pewien że Set działa szybciej dla & i | czy może po prostu s1 i s2 jest o kilka rzędów mniejsze?

Fakt, dzięki za zwrócenie uwagi na ten istotny szczegół. Przy kodzie zmienionym w sposób następujący:

SIZE = 10_000_000 a = [] SIZE.times{|i| a << rand(SIZE*1000)}
dostaję następujące wyniki

user system total real set & 8.620000 0.010000 8.630000 ( 8.634206) set | 15.360000 0.360000 15.720000 ( 15.716081) array & 15.780000 0.050000 15.830000 ( 15.825336) array | 20.980000 0.050000 21.030000 ( 21.018857)
rozmiary zbiorów są następujące

4998720 4998786
czyli zalewie o kilka promili mniejsze niż rozmiary tablic.

Ale wniosek dla obu przypadków jest ten sam - jeśli robimy operacje na zbiorach (w szczególności jeśli wiele elementów może się powtórzyć), to używanie Set jest jak najbardziej uzasadnione.

Z lenistwa nie sprawdziłem sam :wink: Ciekawe że dla & różnica jest nadal bardzo duża. Wieczorem sprawdze z czego może wynikać ta zależność.