The following iterative sequence is defined for the set of positive integers:
n → n/2 (n is even)
n → 3n + 1 (n is odd)
Using the rule above and starting with 13, we generate the following sequence:
13 → 40 → 20 → 10 → 5 → 16 → 8 → 4 → 2 → 1
It can be seen that this sequence (starting at 13 and finishing at 1) contains 10 terms. Although it has not been proved yet (Collatz Problem), it is thought that all starting numbers finish at 1.
Which starting number, under one million, produces the longest chain?
NOTE: Once the chain starts the terms are allowed to go above one million.
“Each problem has been designed according to a “one-minute rule”, which means that although it may take several hours to design a successful algorithm with more difficult problems, an efficient implementation will allow a solution to be obtained on a modestly powered computer in less than one minute.”
“Każdy problem został zaprojektowany zgodnie z “zasadą jednej minuty”, co oznacza, że chociaż to może potrwać kilka godzin projektowania pomyślnego algorytm z trudniejszych problemów, sprawnego wykonania pozwoli na rozwiązanie, które mają być uzyskane na skromnie powered komputer w mniej niż jedna minuta.”
Ruby to język skryptowy, więc nie będzie tak wydajny jak C++. Jednak można to odpalić w czasie < 4 sek (virtualbox ubuntu na vista e6300@3.4 6gb ram). Więc nie jest źle.
[code=terminal]projecteuler: Problem 14
user system total real
3.710000 0.340000 4.050000 ( 4.229409)
Q:Which starting number, under one million, produces the longest chain?
A: 837799[/code]
bez VB jest chyba szybciej ale nie mam obecnie dostępu
ps. to nie mój skrypt odpaliłem, mój wykonywał się dłużej.
[quote=luckboy]Ten problem da się rozwiązać w mniej nisz jedną sekundę wykorzystując C++ :lol:.
Wiem piszemy w ruby.[/quote]
Daj mi troszkę szybszy procek i ruby też wyciągnie…
Oto moje 2 rozwiązanie: pierwsze szybsze (brzydsze), drugie wolniejsze, ale za to jakie…
cache = Array.new(1_000_000)
cache[0] = cache[1] = 1
max_len = 1
max_i = 1
len = 0
stack = []
1_000_000.times do |i|
next if cache[i]
while cache[i].nil?
stack.push(i)
i = i % 2 == 0 ? (i >> 1) : (3 * i + 1)
end
len = cache[i]
while i = stack.pop
len += 1
cache[i] = len if i < 1_000_000
if (len > max_len && i < 1_000_000)
max_len = len
max_i = i
end
end
end
W Ruby można to policzyć szybciej dodając cache i sprawdzając tylko liczby nieparzyste (liczba musi być nieparzysta aby wywołać sekwencję, inaczej jest tylko n/2 aż do 1):
[code=ruby]$cache = { }
def collatz(n)
if f = $cache[n]
return f
end
i = 1
while n > 1
if f = $cache[n]
return $cache[n] + i
end
i += 1
n%2==0 ? n /= 2 : n = 3*n+1
end
i
end
i = 0
max = 0
(1…999_999).step(2) do |x|
j = collatz(x)
max, i = x, j if j > i
$cache[x] = j
end
p max[/code]
Wynik na moim (wolnym) sprzęcie:
ruby 1.8.6 - 12 sek
ruby 1.9 - 6 sek
Nie mogę porównać czasu wersji teamon’a bo memoize-1.2.3 wysypuje się zarówno na 1.8.6 (stack level too deep) jak i 1.9 (implicit argument passing of super from method defined by define_method() is not supported) przynajmniej u mnie.
Fakt, który o dziwo można łatwo przeoczyć o 1 w nocy (zyskałem i tak tylko marną 1 sekundę), Twoje rozw. 2 jest krótko mówiąc imponujące i w dodatku o 1 sek, szybsze od mojego rozw. 1 wysypuje się na moim ruby 1.9.1 (2008-12-30 patchlevel-0 revision 21203) bignum too big to convert into `long’ (RangeError) - konieczny update
while cache[i].nil?
stack << i
i = i % 2 == 0 ? (i >> 1) : (3 * i + 1)
print "cache[#{i}].nil? \n"
end
w efekcie daje:
"cache[1654740 898].nil? "
"cache[827370449].nil? "
"cache[2482111348].nil? "
14rad.rb:24:in `[]': bignum too big to convert into `long' (RangeError)
from 14rad.rb:24:in `block in <main>'
from 14rad.rb:19:in `times'
from 14rad.rb:19:in `<main>'
samo uruchomienie:
[code=terminal]irb(main):003:0> p cache[2482111348].nil?
RangeError: bignum too big to convert into `long’ (RangeError)[/code]
Do tej pory myślałem, że wskaźnik tablicy da sie zrobić z 11 cyfrowej liczby, ale nigdy nie miałem z tym doczynienia.
Efekt dostaje ten sam w 1.8.6 i 1.9.1 p0 niezależnie od systemu. To jest jakiś bug?
ps. Kiwi wypas, na moim VB:
[code=terminal]wojak@wojak-desktop:~/Pulpit/railsapps$ time ruby 14kiwi.rb
837799
Przy wykorzystaniu tablicy i korzystaniu z danych historycznych obliczenia zajęły mi około 15 sekund.
[code=Ruby]a = [0, 1]
(2…999999).each {|n|
i = 0
s = n
while n >= s
i += 1
if n % 2 == 0
n = n / 2
else
n = 3 * n + 1
end
end
a[s] = a[n] + i
}
l = a.sort.last
p “element=#{a.index(l)}, długość=#{l}”