Problem 12

Problem 12

The sequence of triangle numbers is generated by adding the natural numbers. So the 7^(th) triangle number would be 1 + 2 + 3 + 4 + 5 + 6 + 7 = 28. The first ten terms would be:

1, 3, 6, 10, 15, 21, 28, 36, 45, 55, …

Let us list the factors of the first seven triangle numbers:

 1: 1
 3: 1,3
 6: 1,2,3,6
10: 1,2,5,10
15: 1,3,5,15
21: 1,3,7,21
28: 1,2,4,7,14,28

We can see that 28 is the first triangle number to have over five divisors.

What is the value of the first triangle number to have over five hundred divisors?

Trudny problem ? :wink:

Poniższe rozwiązanie dla 1.8.7/1.9 nie wykona się niestety w czasie godnym do zaakceptowania:

[code=ruby]def triangle
a,b = 1,1
loop do
yield a
b += 1
a = a + b
end
end

def num_divisors(n)
(1…n).inject(0) { |acc,x| acc + (n % x == 0 ? 1 :0)}
end

puts enum_for(:triangle).take_while { |x| num_divisors(x) <= 501 }.last[/code]
Potrzebujemy czegoś sprytniejszego :slight_smile:

Jaki tam od razu trudny :P.

hosiawak, Twój warunek nie jest poprawny. Użyj find zamiast take_while i zmień warunek na num_divisors(x) > 500.

Moje rozwiązanie opiera się na prawie, że po rozłożeniu liczby x na czynniki pierwsze i zapisie tej liczby w postaci:
x = p1^e1 + p2^e2 + … + pn^en
gdzie p1, p2… pn to czynniki pierwsze, zaś e1…en to ilość wystąpień czynnika w rozkładzie pierwszym liczby
to liczba dzielników liczby wynosi
(e1 + 1) * (e2 + 1) * … * (en + 1)

[code=ruby]def divisors(n)
d = []
2.upto(Math.sqrt(n).floor) do |i|
p = 0
while n % i == 0
p += 1
n /= i
end
if p > 0
d << p
end
end
return d.empty? ? 2 : (d.inject(1) {|acc, pow| acc * (pow + 1) } )
end

def triangle
a, b = 1, 1
loop do
yield a
b += 1
a += b
end
end

puts enum_for(:triangle).find {|x| divisors(x) > 500 }[/code]

[quote=radarek]Jaki tam od razu trudny :P.

hosiawak, Twój warunek nie jest poprawny. Użyj find zamiast take_while i zmień warunek na num_divisors(x) > 500.

Moje rozwiązanie opiera się na prawie, że po rozłożeniu liczby x na czynniki pierwsze i zapisie tej liczby w postaci:
x = p1^e1 + p2^e2 + … + pn^en
gdzie p1, p2… pn to czynniki pierwsze, zaś e1…en to ilość wystąpień czynnika w rozkładzie pierwszym liczby
to liczba dzielników liczby wynosi
(e1 + 1) * (e2 + 1) * … * (en + 1)[/quote]
radarek znowu pozamiatał :slight_smile:

Chyba mi się udało zejść poniżej minuty :slight_smile: . Sprawdź sam hosiawak czy mój skrypt na twoim sprzęcie zejdzie poniżej minuty.

[code=ruby]#!/usr/bin/env ruby
N = 500

def divisions2(x)
x /= 2 if x%2 == 0
(1…Math.sqrt(x).floor).inject([]) do |t, d|
t += (x%d == 0 ? (x/d == d ? [d] : [d, x/d]) : [])
end
end

i = 1
td1 = divisions2(1)
while true
x = (i*(i+1))/2
td2 = divisions2(i+1)
hd = { }
y = td1.inject(0) do |s1, d1|
s1+td2.inject(0) do |s2, d2| i
unless hd[d1d2]
hd[d1
d2] = true
s2+1
else
s2
end
end
end
break if y > N
td1 = td2
i += 1;
end

puts x[/code]

@luckboy, udało ci się zejść poniżej 2 sekund, chociaż wątpię, że radarek to optymalizował pod kątem szybkości.

time /opt/ruby19/bin/ruby radarek.rb
76576500

real 0m14.226s
user 0m13.849s
sys 0m0.100s

time /opt/ruby19/bin/ruby luckboy.rb
76576500

real 0m1.933s
user 0m1.396s
sys 0m0.024s

Pod jakim sprzętem (procesor i jego częstotliwość)?

Pentium M 1.4 GHz

Dziwne bo na moim AMD Turion64 1600MHz wyciaga 5.553s. Ale ja używam ruby w wersji 1.8.7.

Po prostu procki Intela są o tyle szybsze od AMD :wink:

A tak na serio to różnica wynika stąd, że testowałem przy użyciu Ruby 1.9 (co można było wywnioskować po ścieżce :slight_smile: )

Korzystając z rozwiązania radarka i STDLIB ruby1.9:

require 'prime'

def triangle 
  a, b = 1, 1
  loop do 
    yield a
    b += 1
    a += b
  end
end

p enum_for(:triangle).find {|x| x.prime_division.collect(&:last).inject(1) {|product, e| product * (e + 1)} > 500 } 

Wyniki na moim e8200:

real 0m1.490s user 0m1.488s sys 0m0.000s
A i kod ładniejszy i krótszy :slight_smile:

a jaki masz procesor z jaka czestotliwoscia?

i znow ktos jest lepszy ode mnie :rolleyes:

Heh chciałbym. Ale mój procesor to jak napisałem e8200 - 2 rdzenie po 2,66 GHz. Później jak będe miał do niego dostęp to sprawdze jak szybkie jest twoje rozwiązanie.

found = false i = 0 while !found i += 1 t = (1..i).inject {|sum,n| sum + n} found = true if (1..t).select {|j| t % j == 0}.size > 100 end p t
73920

Może nie najszybsze, ale na pewno nakrótsze z podanych:

require"mathn";x=2;a=3;a+=x+=1 until a.prime_division.map{|i|i[1]+1}.inject(:*)>500;a

(u mnie wynik pojawia się po niemal 2 minutach)