Studium  Speedcubing  Elektronik  Project Euler  Diablo II  Impressum 

Project Euler

Project Euler

Bei Project Euler handelt es sich um eine Webseite mit einer Art "Programmierwettbewerb" für mehr oder weniger komplexe mathematische Problemstellungen, in erster Linie zahlentheoretischer Natur. Das Problem (bzw. der Reiz) der meisten Aufgaben ist, dass sie prinzipiell sehr leicht zu lösen sind, die Lösung, auf die man "direkt" kommt aber meistens extrem ineffizient ist und man sich etwas mit dem mathematischen Background beschäftigen muss, um den eigenen Algorithmus zu optimieren. Da ich schon lange die Programmiersprache Ruby lernen wollte war das eine gute Gelegenheit für mich.

Die meisten meiner Lösungen sind nicht besonders effizient (und teilweise vermutlich auch mathematisch nicht wirklich optimal). Mein Ziel war es Ruby zu lernen, nicht die Aufgaben möglichst geschickt zu lösen. Dennoch sind fast alle meine Lösungen in deutlich weniger als einer Minute (die meisten sogar in weniger als einer Sekunde auf einem halbwegs aktuellen Prozessor) ausführbar (und das obwohl Ruby eine relativ langsam interpretierte Sprache ist).

Viele der Aufgaben sind etwas eleganter lösbar, wenn man Ruby Libraries benutzt (z.B. die Prime Klasse aus 'mathn'). Ich habe darauf absichtlich verzichtet, da ich alle Algorithmen selbst implementieren wollte.

Achtung: Viele Leute beschweren sich darüber, dass im Netz Lösungen zu den Aufgaben veröffentlicht werden. Ich habe kein Problem damit, im Gegenteil, ich finde es sinnvoll, dass Lösungen veröffentlicht werden damit man sich auch andere Ansätze anschauen und dadurch noch mehr über das Problem lernen kann. Bis auf ein paar wenige Ausnahmen habe ich alle Probleme vollständig selbst gelöst und mir erst im Nachhinein die Programme anderer angeschaut. Wer die Lösungen einfach nur kopiert um möglichst viele Probleme abzuhaken ist selbst schuld! Dadurch verdirbt man sich nur den Spass ohne jeglichen Nutzen davon zu haben.

Gelöste Probleme: 80 (Stufe 2 von 5).

001  002  003  004  005  006  007  008  009  010  011  012  013  014  015  
016  017  018  019  020  021  022  023  024  025  026  027  028  029  030  
031  032  033  034  035  036  037  038  039  040  041  042  043  044  045  
046  047  048  049  050  051  052  053  054  055  056  057  058  059  060  
061  062  063  064  065  066  067  068  069  070  071  072  073  074  075  
076  077  078  079  080  081  082  083  084  085  086  087  088  089  090  
091  092  093  094  095  096  097  098  099  100  101  102  103  104  105  
106  107  108  109  110  111  112  113  114  115  116  117  118  119  120  
121  122  123  124  125  126  127  128  129  130  131  132  133  134  135  
136  137  138  139  140  141  142  143  144  145  146  147  148  149  150  
151  152  153  154  155  156  157  158  159  160  161  162  163  164  165  
166  167  168  169  170  171  172  173  174  175  176  177  178  179  180  
181  182  183  184  185  186  187  188  189  190  191  192  193  194  195  
196  197  198  199  200  201  202  203  204  205  206  

Problem 1

Find the sum of all the multiples of 3 or 5 below 1000.
n = 1000
def gauss(n) n*(n+1)/2 end
puts 3*gauss((n-1)/3) + 5*gauss((n-1)/5) - 15*gauss((n-1)/15)
Stichwort kleiner Gauss.

Problem 2

Find the sum of all the even-valued terms in the Fibonacci sequence which do not exceed four million.
a,b,sum = 1,1,0

while a < 4*(10**6) do
  sum += a if a%2==0
  a, b = b, a+b
end

puts sum

Problem 3

What is the largest prime factor of the number 600851475143 ?
n = 600851475143
i = 2

while n>1 do
  n, i = n/i, i-1 if n%i == 0
  i += 1
end

puts i
Diese Lösung gefällt mir sehr gut. Sie ist extrem schnell (O(n1/2 log2(n))), gleichzeitig sehr einfach zu verstehen und benutzt nur indirekt irgendwelche Eigenschaften von Primzahlen. Ein Beweis der Korrektheit findet sich in Primality testing in deterministic polynomial time von Professor Michiel Smid (Seite 24 und 25).

Problem 4

Find the largest palindrome made from the product of two 3-digit numbers.
max = 0

for i in [*100..999].reverse do
  for j in [*i..999].reverse do
    next if ( i%11 != 0 ) and ( j%11 != 0 )
    x = i*j
    max = [max,x].max if x.to_s == x.to_s.reverse
  end
end

puts max
Optimierung: Palindrome gerader Länge sind immer durch 11 teilbar.

Problem 5

What is the smallest number that is evenly divisible by all of the numbers from 1 to 20?
def gcd(a,b) return (b==0) ? a : gcd(b, a % b) end
puts [*1..20].inject(1){ |s,x| s*x / gcd(s,x) }
Stichwort kleinstes gemeinsames Vielfaches.

Problem 6

Find the difference between the sum of the squares of the first one hundred natural numbers and the square of the sum.
n = 100
puts ( ( n*(n+1)/2 ) ** 2 ) - ( n*(n+1)*(2*n+1) / 6 )
Stichwort Gaussche Summenformeln.

Problem 7

What is the 10001st prime number?
require 'primetool'
# Nach Primzahlsatz: 10001 <= 120000 / ln(120000) = 10260
p = PrimeTool.new(120000)
puts p.prime(10001)
Stichwort Primzahlsatz und Primzahlsieb.

Problem 8

Find the greatest product of five consecutive digits in the 1000-digit number.
n = <<EOF
73167176531330624919225119674426574742355349194934
96983520312774506326239578318016984801869478851843
85861560789112949495459501737958331952853208805511
12540698747158523863050715693290963295227443043557
66896648950445244523161731856403098711121722383113
62229893423380308135336276614282806444486645238749
30358907296290491560440772390713810515859307960866
70172427121883998797908792274921901699720888093776
65727333001053367881220235421809751254540594752243
52584907711670556013604839586446706324415722155397
53697817977846174064955149290862569321978468622482
83972241375657056057490261407972968652414535100474
82166370484403199890008895243450658541227588666881
16427171479924442928230863465674813919123162824586
17866458359124566529476545682848912883142607690042
24219022671055626321111109370544217506941658960408
07198403850962455444362981230987879927244284909188
84580156166097919133875499200524063689912560717606
05886116467109405077541002256983155200055935729725
71636269561882670428252483600823257530420752963450
EOF

n = n.gsub("\n",'').split(//)

max = 0
for j in 0..n.length-5
  max = [max, [*0..4].map{ |i| n[j+i].to_i }.inject(1){ |t,i| t * i } ].max
end

puts max

Problem 9

There exists exactly one Pythagorean triplet for which a + b + c = 1000. Find the product abc.
for a in 1..997 do
  for b in a..998 do
    c = 1000 - a - b
    if a*a + b*b == c*c
      puts a*b*c
      exit
    end    
  end
end

Problem 10

Find the sum of all the primes below two million.
require 'primetool'
n = 2 * (10**6)
p = PrimeTool.new(n)
puts [*2..n].inject(0){ |s,i| s + ((p.is_prime?(i)) ? i : 0) }

Problem 11

What is the greatest product of four adjacent numbers in any direction (up, down, left, right, or diagonally) in the 20x20 grid?
@data = <<EOF
08 02 22 97 38 15 00 40 00 75 04 05 07 78 52 12 50 77 91 08
49 49 99 40 17 81 18 57 60 87 17 40 98 43 69 48 04 56 62 00
81 49 31 73 55 79 14 29 93 71 40 67 53 88 30 03 49 13 36 65
52 70 95 23 04 60 11 42 69 24 68 56 01 32 56 71 37 02 36 91
22 31 16 71 51 67 63 89 41 92 36 54 22 40 40 28 66 33 13 80
24 47 32 60 99 03 45 02 44 75 33 53 78 36 84 20 35 17 12 50
32 98 81 28 64 23 67 10 26 38 40 67 59 54 70 66 18 38 64 70
67 26 20 68 02 62 12 20 95 63 94 39 63 08 40 91 66 49 94 21
24 55 58 05 66 73 99 26 97 17 78 78 96 83 14 88 34 89 63 72
21 36 23 09 75 00 76 44 20 45 35 14 00 61 33 97 34 31 33 95
78 17 53 28 22 75 31 67 15 94 03 80 04 62 16 14 09 53 56 92
16 39 05 42 96 35 31 47 55 58 88 24 00 17 54 24 36 29 85 57
86 56 00 48 35 71 89 07 05 44 44 37 44 60 21 58 51 54 17 58
19 80 81 68 05 94 47 69 28 73 92 13 86 52 17 77 04 89 55 40
04 52 08 83 97 35 99 16 07 97 57 32 16 26 26 79 33 27 98 66
88 36 68 87 57 62 20 72 03 46 33 67 46 55 12 32 63 93 53 69
04 42 16 73 38 25 39 11 24 94 72 18 08 46 29 32 40 62 76 36
20 69 36 41 72 30 23 88 34 62 99 69 82 67 59 85 74 04 36 16
20 73 35 29 78 31 90 01 74 31 49 71 48 86 81 16 23 57 05 54
01 70 54 71 83 51 54 69 16 92 33 48 61 43 52 01 89 19 67 48
EOF

@data = @data.gsub("\n", " ").split(/ /)

def grid(i,j)
  return 0 if i<0 or i>19 or j<0 or j>19
  return @data[j + 20*i].to_i
end

def prod(i,j,idir,jdir)
  return grid(i,j) * grid(i+idir,j+jdir) * grid(i+2*idir,j+2*jdir) * grid(i+3*idir,j+3*jdir)
end

max = 0
for i in 1..19 do
  for j in 1..19 do
    for idir in [-1,0,1] do
      for jdir in [0,1] do
        next if jdir == idir and idir == 0 
        max = [max, prod(i,j,idir,jdir)].max
      end
    end
  end
end

puts max
Nicht so schön. Mehr oder weniger Brute Force, läuft aber in wenigen Millisekunden.

Problem 12

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

def d(n)
  c,i = 2,2
  # Bis sqrt(n) testen reicht, dann direkt 2 zählen
  while i*i < n do
    c += 2 if n%i == 0
    i += 1
  end
  return c
end

m,t,i = 0,1,1
while m <= limit do
  t = i*(i+1)/2

  # i und i+1 sind teilerfremd und d(n) ist zahlentheoretisch
  a = (i%2==0) ? [i/2,i+1] : [i,(i+1)/2]
  m = d(a[0]) * d(a[1])

  i += 1
end
puts t

Problem 13

Work out the first ten digits of the sum of the following one-hundred 50-digit numbers.
n = %w{
37107287533902102798797998220837590246510135740250
46376937677490009712648124896970078050417018260538
74324986199524741059474233309513058123726617309629
91942213363574161572522430563301811072406154908250
23067588207539346171171980310421047513778063246676
89261670696623633820136378418383684178734361726757
28112879812849979408065481931592621691275889832738
44274228917432520321923589422876796487670272189318
47451445736001306439091167216856844588711603153276
70386486105843025439939619828917593665686757934951
62176457141856560629502157223196586755079324193331
64906352462741904929101432445813822663347944758178
92575867718337217661963751590579239728245598838407
58203565325359399008402633568948830189458628227828
80181199384826282014278194139940567587151170094390
35398664372827112653829987240784473053190104293586
86515506006295864861532075273371959191420517255829
71693888707715466499115593487603532921714970056938
54370070576826684624621495650076471787294438377604
53282654108756828443191190634694037855217779295145
36123272525000296071075082563815656710885258350721
45876576172410976447339110607218265236877223636045
17423706905851860660448207621209813287860733969412
81142660418086830619328460811191061556940512689692
51934325451728388641918047049293215058642563049483
62467221648435076201727918039944693004732956340691
15732444386908125794514089057706229429197107928209
55037687525678773091862540744969844508330393682126
18336384825330154686196124348767681297534375946515
80386287592878490201521685554828717201219257766954
78182833757993103614740356856449095527097864797581
16726320100436897842553539920931837441497806860984
48403098129077791799088218795327364475675590848030
87086987551392711854517078544161852424320693150332
59959406895756536782107074926966537676326235447210
69793950679652694742597709739166693763042633987085
41052684708299085211399427365734116182760315001271
65378607361501080857009149939512557028198746004375
35829035317434717326932123578154982629742552737307
94953759765105305946966067683156574377167401875275
88902802571733229619176668713819931811048770190271
25267680276078003013678680992525463401061632866526
36270218540497705585629946580636237993140746255962
24074486908231174977792365466257246923322810917141
91430288197103288597806669760892938638285025333403
34413065578016127815921815005561868836468420090470
23053081172816430487623791969842487255036638784583
11487696932154902810424020138335124462181441773470
63783299490636259666498587618221225225512486764533
67720186971698544312419572409913959008952310058822
95548255300263520781532296796249481641953868218774
76085327132285723110424803456124867697064507995236
37774242535411291684276865538926205024910326572967
23701913275725675285653248258265463092207058596522
29798860272258331913126375147341994889534765745501
18495701454879288984856827726077713721403798879715
38298203783031473527721580348144513491373226651381
34829543829199918180278916522431027392251122869539
40957953066405232632538044100059654939159879593635
29746152185502371307642255121183693803580388584903
41698116222072977186158236678424689157993532961922
62467957194401269043877107275048102390895523597457
23189706772547915061505504953922979530901129967519
86188088225875314529584099251203829009407770775672
11306739708304724483816533873502340845647058077308
82959174767140363198008187129011875491310547126581
97623331044818386269515456334926366572897563400500
42846280183517070527831839425882145521227251250327
55121603546981200581762165212827652751691296897789
32238195734329339946437501907836945765883352399886
75506164965184775180738168837861091527357929701337
62177842752192623401942399639168044983993173312731
32924185707147349566916674687634660915035914677504
99518671430235219628894890102423325116913619626622
73267460800591547471830798392868535206946944540724
76841822524674417161514036427982273348055556214818
97142617910342598647204516893989422179826088076852
87783646182799346313767754307809363333018982642090
10848802521674670883215120185883543223812876952786
71329612474782464538636993009049310363619763878039
62184073572399794223406235393808339651327408011116
66627891981488087797941876876144230030984490851411
60661826293682836764744779239180335110989069790714
85786944089552990653640447425576083659976645795096
66024396409905389607120198219976047599490197230297
64913982680032973156037120041377903785566085089252
16730939319872750275468906903707539413042652315011
94809377245048795150954100921645863754710598436791
78639167021187492431995700641917969777599028300699
15368713711936614952811305876380278410754449733078
40789923115535562561142322423255033685442488917353
44889911501440648020369068063960672322193204149535
41503128880339536053299340368006977710650566631954
81234880673210146739058568557934581403627822703280
82616570773948327592232845941706525094512325230608
22918802058777319719839450180888072429661980811197
77158542502016545090413245809786882778948721859617
72107838435069186155435662884062257473692284509516
20849603980134001723930671666823555245252804609722
53503534226472524250874054075591789781264330331690
}

puts n.map{|x| x.to_i}.inject(0){ |s,i| s+i }.to_s[0..9]

Problem 14

The following iterative sequence is defined for the set of positive integers:
n -> n/2 (n is even)
n -> 3n + 1 (n is odd)
Which starting number, under one million, produces the longest chain?
n = 10**6

table = { 1 => 1 }
max = [0,0]

# OBdA nur ungerade Zahlen betrachten; Zu jeder geraden gibt es eine
# ungerade, die eine längere Kette erzeugt
1.step(n,2) do |i|

  c,m = 0,i

  while table[m].nil? do
    c += 1
    m = (m%2==0) ? m/2 : 3*m+1
  end

  table[i] = c + table[m]
  max = [table[i],i] if max[0]<table[i]

end

puts max[1]
Stichwort Dynamic Programming.

Problem 15

How many routes are there through a 20x20 grid?
n = 20
table = []

for i in 0..n
  table[i] = []
  table[i][n] = 1
end

for j in 0..n do
  table[n][j] = 1
end

for i in [*0..(n-1)].reverse do
  for j in [*0..(n-1)].reverse do
    table[i][j] = table[i+1][j] + table[i][j+1]
  end
end

puts table[0][0]
Stichwort Dynamic Programming.

Problem 16

What is the sum of the digits of the number 21000?
puts (2**1000).to_s.split(//).inject(0){ |s,x| s+x.to_i }
Naja, das geht bestimmt auch "mathematischer" :-)

Problem 17

If all the numbers from 1 to 1000 ("one thousand") inclusive were written out in words, how many letters would be used?
@names = [ "",
  "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten",
  "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen",
  "eighteen", "nineteen", "twenty"
]

@names[  30] = "thirty"
@names[  40] = "forty"
@names[  50] = "fifty"
@names[  60] = "sixty"
@names[  70] = "seventy"
@names[  80] = "eighty"
@names[  90] = "ninety"
@names[ 100] = "hundred"
@names[1000] = "one thousand"

def name(n)
  return @names[n] if @names[n]
  return name(n/10*10) + "-" + name(n%10) if n<100
  return name(n/100) + " " + name(100) + ( (n%100 > 0) ? (" and " + name(n%100)) : "" )
end

sum = 3 # "one" bei 100 fehlt ("hundred")
for i in 1..1000 do
  sum += name(i).gsub(/ |-/, "").length
end
puts sum

Problem 18

Find the maximum total from top to bottom of the triangle below.
triangle = [
  [75],
  [95, 64],
  [17, 47, 82],
  [18, 35, 87, 10],
  [20, 04, 82, 47, 65],
  [19, 01, 23, 75, 03, 34],
  [88, 02, 77, 73, 07, 63, 67],
  [99, 65, 04, 28, 06, 16, 70, 92],
  [41, 41, 26, 56, 83, 40, 80, 70, 33],
  [41, 48, 72, 33, 47, 32, 37, 16, 94, 29],
  [53, 71, 44, 65, 25, 43, 91, 52, 97, 51, 14],
  [70, 11, 33, 28, 77, 73, 17, 78, 39, 68, 17, 57],
  [91, 71, 52, 38, 17, 14, 91, 43, 58, 50, 27, 29, 48],
  [63, 66, 04, 68, 89, 53, 67, 30, 73, 16, 69, 87, 40, 31],
  [04, 62, 98, 27, 23,  9, 70, 98, 73, 93, 38, 53, 60, 04, 23]
]

(triangle.length-2).downto(0) { |i|
  for j in 0..(triangle[i].length-1) do
    triangle[i][j] += [triangle[i+1][j], triangle[i+1][j+1]].max
  end
}

puts triangle[0][0]

Problem 19

How many Sundays fell on the first of the month during the twentieth century?
def dow(q,m,y)
  if m<=2 then m+=12; y-=1; end
  j = y / 100
  k = y - 100*j
  return ( q + ((m+1)*26)/10 + k + k/4 + j/4 - 2 * j ) % 7
end

sum = 0
for month in 1..12 do
  for year in 1..100 do
    sum += 1 if dow(1,month,1900+year) == 1
  end
end
puts sum
Idee: Zellers Kongruenz. Hätte man einfacher haben können mit den Ruby Date Funktionen, wollte ich aber nicht.

Problem 20

Find the sum of digits in 100!
def fac(n) return (n==1) ? 1 : n*fac(n-1) end
puts fac(100).to_s.split(//).inject(0){ |s,x| s+x.to_i }
Siehe Problem 16.

Problem 21

Let d(n) be defined as the sum of proper divisors of n. If d(a) = b and d(b) = a, where a != b, then a and b are an amicable pair and each of a and b are called amicable numbers. Evaluate the sum of all the amicable numbers under 10000.
n = 10**4

def dsum(n)
  c,i = 1,2
  # Nur bis sqrt(n) testen und dafür dann doppelt zählen
  while i*i < n do
    c += i + n/i if n%i == 0
    i += 1
  end
  return c
end

sum = 0

for i in 1..(n-1) do
  j = dsum(i)
  next if i==j
  sum += i if i == dsum(j)
end

puts sum

Problem 22

Using names.txt, a text file containing over five-thousand first names, begin by sorting it into alphabetical order. Then working out the alphabetical value for each name, multiply this value by its alphabetical position in the list to obtain a name score. For example, when the list is sorted into alphabetical order, COLIN, which is worth 3 + 15 + 12 + 9 + 14 = 53, is the 938th name in the list. So, COLIN would obtain a score of 938 × 53 = 49714. What is the total of all the name scores in the file?
d = File.open("22_names.txt").read.gsub('"','').split(/,/).sort
puts d.each_index.inject(0){ |s,i| s + ( d[i].split(//).inject(0){ |s,c| s+c[0]-"@"[0] } * (i+1) ) }
Schön kurz! Fast so unlesbar wie Perl Code :-)

Problem 23

It can be shown that all integers greater than 28123 can be written as the sum of two abundant numbers. Find the sum of all the positive integers which cannot be written as the sum of two abundant numbers.
max = 28123

def dsum(n)
  return 0 if n==1
  s, i = 1, 2
  while i*i < n do
    s += i + n/i if n%i == 0
    i += 1
  end
  s += i if i*i == n
  return s
end

abundants = []
n = []
0.upto(max-1) { |i| n[i] = i+1; abundants << n[i] if dsum(n[i])>n[i] }
l = abundants.length

0.upto(l-1) do |i|
  i.upto(l-1) do |j|
    k = abundants[i] + abundants[j] - 1
    n[k] = 0 if k < max
  end
end

puts n.inject(0){ |s,k| s+k }
Doofe Lösung für eine doofe Aufgabe.

Problem 24

What is the millionth lexicographic permutation of the digits 0, 1, 2, 3, 4, 5, 6, 7, 8 and 9?
n = 10**6

def fac(n)
  return 1 if n==1
  return n*fac(n-1)
end

def perm(a,k)
  return a[0] if a.length == 1
  f = fac(a.length - 1)
  i = k / f
  s = a[i]
  a.delete_at(i)
  return [s, perm(a, k - f * i)]
end

puts perm([0,1,2,3,4,5,6,7,8,9], n-1).to_s
Hier muss ich zugeben, dass ich nur auf extrem langsame Lösungen gekommen bin und daher etwas gegooglet habe. Die Idee ist von Chris Smith geklaut.

Problem 25

What is the first term in the Fibonacci sequence to contain 1000 digits?
a = b = i = 1
while a.to_s.length < 1000 do a, b, i = b, a+b, i+1 end
puts i

Problem 26

Find the value of d < 1000 for which 1/d contains the longest recurring cycle in its decimal fraction part.
n = 1000

def divrest(a,b)

  rems = []
  r = 1

  while r != 0 do
    k = a/b
    
    if k==0 then
      a *= 10
      k = a/b
    end

    r = a%b

    if not rems.include?(r) then
      rems << r
    else
      break
    end

    a = r
  end

  return rems.length
end

max = [0,0]
1.upto(n-1){ |i|
  k = divrest(1,i)
  max = [k,i] if k>max[0]
}

puts max[1]
Mögliche Optimierung: Zahlen mit endlicher Entwicklung von vornerein ausschliessen (das sind genau die, die keine anderen Primteiler als 2 oder 5 haben).

Problem 27

Euler published the remarkable quadratic formula: n2 + n + 41. It turns out that the formula will produce 40 primes for the consecutive values n = 0 to 39. Considering quadratics of the form: n2 + an + b, where |a| < 1000 and |b| < 1000. Find the product of the coefficients, a and b, for the quadratic expression that produces the maximum number of primes for consecutive values of n, starting with n = 0.
require 'primetool'
maxab = 1000-1
@pt = PrimeTool.new(100*100 + 100*maxab + maxab)

def primes(a,b)
  primes = 0
  1.upto(100){ |n|
    if @pt.is_prime?(n*(n+a)+b) then
      primes += 1
    else
      break
    end
  }
  primes
end

max = [0,0,0]
@pt.primes.select{ |b| b <= maxab }.each do |b|
  @pt.primes.each do |p|  
    a = p-b-1
    next if a < -maxab
    break if a > maxab
    k = primes(a,b)
    max = [k,a,b] if max[0] < k
  end
end
puts max[1]*max[2]
Sehr interessante Aufgabe (siehe auch hier)! Ideen: an n=0 sieht man, dass b prim sein muss (und positiv) und an n=1 sieht man, dass a+b+1 ebenfalls prim sein muss.

Problem 28

Starting with the number 1 and moving to the right in a clockwise direction a n by n spiral is formed. What is the sum of both diagonals in a 1001 by 1001 spiral?
n = 1001

diagonals = []
sum, k = 1, 2

while true do
  break if diagonals.length > 2*n-1
  4.times{ |i| diagonals << sum+i*k }
  sum += 4*k
  k += 2
end

puts diagonals[0..2*n-2].inject(0){ |s,x| s+x }
Nette Aufgabe, nicht soo schöne Lösung, aber schnell (< 1 ms).

Problem 29

How many distinct terms are in the sequence generated by ab for 2 <= a,b <= 100?
puts (2..100).map{ |i| (2..100).map{ |j| [i**j, j**i] } }.flatten.sort.uniq.length
Ziemlich straight-forward und schön kurz :-)

Problem 30

Find the sum of all the numbers that can be written as the sum of fifth powers of their digits.
@values = [0,1,32,243,1024,3125,7776,16807,32768,59049]
@table = []

def sum(n)
  return @values[n] if n<10
  return @table[n] if @table[n]
  @table[n] = @values[n%10] + sum(n / 10)
end

s = 0
(3..@values[9]*6).each{ |i| s += i if sum(i) == i }
puts s
Gleicher Algorithmus wie bei 34.

Problem 31

How many different ways can £2 be made using any number of coins?
n = 200
i = 0

0.step(n,200) do |a|
  a.step(n,100) do |b|
    b.step(n,50) do |c|
      c.step(n,20) do |d|
        d.step(n,10) do |e|
          e.step(n,5) do |f|
            f.step(n,2) do |g|
              i += 1
            end
          end
        end
      end
    end
  end
end

puts i

Problem 32

The product 7254 is unusual, as the identity, 39 x 186 = 7254, containing multiplicand, multiplier, and product is 1 through 9 pandigital. Find the sum of all products whose multiplicand/multiplier/product identity can be written as a 1 through 9 pandigital.
def dups?(a)
  0.upto(a.length-2) do |i|
    (i+1).upto(a.length-1) do |j|
      return true if a[i] == a[j]
    end
  end
  return false
end

hits = {}

2.upto(98) do |a|
  as = a.to_s
  next if dups?(as)
  next if as.include?("0")
  minb = (4 - as.length == 3) ? 1234 : 123
  maxb = (5 - as.length == 4) ? 9876 : 987
  minb.upto(maxb) do |b|
    bs = b.to_s
    next if bs.include?("0")
    c=a*b
    hits[c] = true if (as + bs + c.to_s).split("").sort.join("") == "123456789"
  end
end

puts hits.map{ |k,v| k }.inject(0){ |s,k| s+k }
Idee: Durch Abschätzungen des Logarithmus zur Basis 10 und der Gaussklammer, kann man leicht zeigen, dass 4 - length(a) <= length(b) <= 5 - length(a) gelten muss. Das schränkt den Suchraum bereits deutlich ein. Weiterhin müssen keine Zahlen getestet werden, die die Ziffer 0 enthalten oder die eine Ziffer doppelt enthalten.

Problem 33

The fraction 49/98 is a curious fraction (...) 49/98 = 4/8, which is correct (...). There are exactly four non-trivial examples of this type of fraction, less than one in value, and containing two digits in the numerator and denominator. If the product of these four fractions is given in its lowest common terms, find the value of the denominator.
def gcd(a,b) return (b==0) ? a : gcd(b, a % b) end

prod = [1,1]

for a in 1..9 do
  for b in a..9 do
    for c in (a+1)..9 do
      if (9*a+b)*c == 10*a*b then
        prod[0] *= a
        prod[1] *= c
      end
    end
  end
end

puts prod[1] / gcd(prod[0], prod[1])
Winzige Optimierung: (10a+b)/(10b+c) = a/c gilt genau dann, wenn (9a+b)c = 10ab.

Problem 34

Find the sum of all numbers which are equal to the sum of the factorial of their digits.
@fac = [1,1,2,6,24,120,720,5040,40320,362880]
@table = []

def facsum(n)
  return @fac[n] if n<10
  return @table[n] if @table[n]
  @table[n] = @fac[n%10] + facsum(n / 10)
end

s = 0
(3..@fac[9]*7).each{ |i| s += i if facsum(i) == i }
puts s
Idee: 7*9! ist eine obere Schranke, denn ab 7 Ziffern ist die größtmögliche Zahl (alle Ziffern 9) größer als die größtmögliche Summe der Fakultäten der Ziffern (Anzahl Ziffern mal 9!). Als kleine Optimierung werden noch Summen in einer Tabelle gespeichert (hat bei mir fast einen Faktor von 3 ausgemacht).

Problem 35

The number, 197, is called a circular prime because all rotations of the digits: 197, 971, and 719, are themselves prime. How many circular primes are there below one million?
require 'primetool'
n = 1000000
pt = PrimeTool.new(n)
primes = pt.primes

def rotate(s)
  s = "#{s[-1,1]}#{s.chop}"
end

count = 0

for p in primes do

  s = p.to_s
  count += 1

  for i in 1..s.length-1 do  
    s = rotate(s)
    if not pt.is_prime?(s.to_i) then
      count -= 1
      break
    end  
  end

end

puts count
Diese Lösung gefällt mir nicht wirklich, sie ist viel zu langsam. Eine mögliche Optimierung: Die Dezimalentwicklung einer zirkulären Primzahl kann niemals die Ziffern 0,2,4,5,6 oder 8 enthalten.

Problem 36

Find the sum of all numbers, less than one million, which are palindromic in base 10 and base 2.
puts [*1..10**6].select{ |i| i.to_s == i.to_s.reverse and i.to_s(2) == i.to_s(2).reverse }.inject(0){ |s,x| s+x }
Ruby ist toll :-)

Problem 37

The number 3797 has an interesting property. Being prime itself, it is possible to continuously remove digits from left to right, and remain prime at each stage: 3797, 797, 97, and 7. Similarly we can work from right to left: 3797, 379, 37, and 3. Find the sum of the only eleven primes that are both truncatable from left to right and right to left. (NOTE: 2, 3, 5, and 7 are not considered to be truncatable primes)
require 'primetool'

@trunc = [ {}, {} ]
@hits = []

def trunc(n,i)

  return if n > 1000000

  if PrimeTool.is_prime_trial?(n) then
    @trunc[i][n] = true
  else
    @trunc[i][n] = false
    return
  end

  for a in [1,2,3,5,7,9] do
    if i==0 then
      next if a == n.to_s[-1]
      trunc((n.to_s + a.to_s).to_i, 0)
    else
      next if a == n.to_s[0]
      trunc((a.to_s + n.to_s).to_i, 1)
    end
  end

  @hits << n if @trunc[0][n] and @trunc[1][n] and n%10 != 1 and n.to_s[0,1] != "1" and n > 10

end

0.upto(9) { |i| trunc(i,0); trunc(i,1) }
puts @hits.inject(0){ |s,x| s+x }
Sehr schöne Aufgabe mit einer sehr schnellen Lösung. Naiver Ansatz: Liste von Primzahlen erstellen (mit einem Primzahlsieb), dann dadrin die truncatables rausfischen. Leider viel zu langsam. Besserer Ansatz: Alle truncatable Primes on-the-fly generieren (mit Trial Division als Test statt dem Sieb) und dabei ausnutzen, dass diese nicht die Ziffern 0, 4, 6 und 8 enthalten können (sonst ist eine der Zahlen gerade), nicht mit 1 anfangen und nicht mit 1 enden können (1 ist nicht prim) und nicht zwei aufeinander folgende gleiche Ziffern enthalten können (sonst ist eine der Zahlen durch 11 teilbar).

Problem 39

If p is the perimeter of a right angle triangle {a,b,c}, which value, for p <= 1000, has the most solutions?
n = 1000
sols = []

1.upto(n) do |a|
  a.upto(n) do |b|
    c = Math.sqrt(a*a + b*b)
    next unless c == c.floor
    sols << [a,b,c.floor] if a+b+c <= n
  end
end

sols = sols.map{ |p| p[0]+p[1]+p[2] }
count = sols.inject(Hash.new(0)){ |h,v| h[v] += 1; h }
puts sols.sort_by{ |v| count[v] }.last
Schöne Aufgabe. Erste Idee: Für alle p<=1000 alle möglichen pythagoräischen Tripel generieren. Das war allerdings viel zu langsam. Neue Idee: Alle möglichen Tripel generieren, deren Summe kleiner als 1000 ist, und dann hinterher das Maximum suchen.

Problem 38

What is the largest 1 to 9 pandigital 9-digit number that can be formed as the concatenated product of an integer with (1,2, ... , n) where n > 1?
max = 0
1.upto(9999) do |k|

  digits = []

  i=0
  while i+=1 and digits.length < 9 do
    new = (k*i).to_s.split(//)
    break if new.include?("0")
    break if new.uniq.length != new.length
    break if digits&new != []
    digits.concat new
    max = [ max, digits.join("").to_i ].max if digits.length == 9
  end

end

puts max

Problem 40

An irrational decimal fraction is created by concatenating the positive integers. If dn represents the nth digit of the fractional part, find the value of the following expression: d1 × d10 × d100 × d1000 × d10000 × d100000 × d1000000
digits = [*0..10**6].to_s
puts [*0..6].inject(1){ |s,x| s * digits[10**x,1].to_i }
Eigentlich hätte ich gerne eine Formel gehabt um die Ziffern direkt zu berechnen (ohne ALLE anderen auch zu speichern). Habe dann aber aufgegeben, als ich gemerkt habe, dass es auf diese (dumme) Weise immernoch recht schnell (~1 Sekunde) ist.

Problem 41

What is the largest n-digit pandigital prime that exists?
require 'primetool'
def perm(k,s) 2.upto(s.length){|j| s[k%j], s[j-1], k = s[j-1], s[k%j], k / j } ; s end

s = [*1..7].to_s
(0..5039).map{ |i| perm(i,s).to_i }.sort.reverse.each do |n|
  if PrimeTool.is_prime_trial?(n) then
    puts n
    break
  end
end
Idee: Die Zahl kann weder 8-pandigital noch 9-pandigital sein, denn solche Zahlen haben eine Quersumme von 3 und sind damit selbst durch 3 teilbar, also nicht prim. (Anmerkung zum Code: 5039 = 7!-1. perm(k,s) erzeugt für jedes 0 <= k < s.length! eine eindeutige Permutation von s)

Problem 42

Using words.txt, a 16K text file containing nearly two-thousand common English words, how many are triangle words (sum of alphabetical values of letters is a triangle number)?
words = File.open('42_words.txt').read.gsub('"', '').split(/,/)
words = words.map{ |s| s.split(//).inject(0){ |s,x| s + ( x[0] - "@"[0] ) } }
t,i  = [1], 1
t << t.last + (i+=1) while t.last <= words.max
puts words.select{ |w| t.include?(w) }.length

Problem 43

The number, 1406357289, is a 0 to 9 pandigital number because it is made up of each of the digits 0 to 9 in some order, but it also has a rather interesting sub-string divisibility property. Let d1 be the 1st digit, d2 be the 2nd digit, and so on. In this way, we note the following: Find the sum of all 0 to 9 pandigital numbers with this property.
digits = [0,1,2,3,4,5,6,7,8,9]
sum = 0

digits.each do |d1|
  (digits - [d1]).each do |d2|
    (digits - [d1, d2]).each do |d3|
      ((digits - [d1, d2, d3]) & [0,2,4,6,8]).each do |d4|
        (digits - [d1, d2, d3, d4]).each do |d5|
          next if (d3+d4+d5) % 3 != 0
          ((digits - [d1,d2,d3,d4,d5]) & [0,5]).each do |d6|
            (digits - [d1,d2,d3,d4,d5,d6]).each do |d7|
              next if (100*d5 + 10*d6 + d7) % 7 != 0
              (digits - [d1,d2,d3,d4,d5,d6,d7]).each do |d8|
                next if (100*d6 + 10*d7 + d8) % 11 != 0
                (digits - [d1,d2,d3,d4,d5,d6,d7,d8]).each do |d9|
                  next if (100*d7 + 10*d8 + d9) % 13 != 0
                  (digits - [d1,d2,d3,d4,d5,d6,d7,d8,d9]).each do |d10|
                    next if (100*d8 + 10*d9 + d10) % 17 != 0
                    sum += [d1,d2,d3,d4,d5,d6,d7,d8,d9,d10].join("").to_i
                  end
                end
              end
            end
          end
        end
      end
    end
  end
end

puts sum
Ziemlich straight-forward und recht schnell.

Problem 44

Find the pair of pentagonal numbers, Pj and Pk, for which their sum and difference is pentagonal and D =|Pk - Pj| is minimised; what is the value of D?
pentas = {}
p = k = 0

while true do

  p += 3*k + 1
  k += 1

  pentas.each_key do |q|
    if pentas[p-q] and pentas[p-q-q] then
      puts "#{p-2*q}"
      exit
    end
  end

  pentas[p] = true

end
Idee: Abstand zwischen Pk und Pk+1 ist 3k+1.

Problem 45

Find the next triangle number (> 40755) that is also pentagonal and hexagonal.
def H(n) n*(2*n-1) end

def penta?(x)
  k = Math.sqrt(24*x+1).floor
  return ( k**2 == (24*x+1) and (k+1)%6 == 0 )
end

i=143
while true do
  i += 1
  break if penta?(H(i))
end
puts H(i)
Sehr schöne Aufgabe! Idee: Jede hexagonale Zahl ist eine Dreieckszahl, also können die Dreieckszahlen komplett ignoriert werden. Ausserdem ist eine natürliche Zahl n genau dann pentagonal, wenn sqrt(24n+1)+1 eine durch 6 teilbare natürliche Zahl ist.

Problem 46

What is the smallest odd composite that cannot be written as the sum of a prime and twice a square?
require 'primetool'
pt = PrimeTool.new(10000)

k=35
while k += 2 do

  hit = false

  pt.primes[1..-1].each do |p|

    break if p > k
    q = ( k - p ) / 2
    
    if Math.sqrt(q).floor ** 2 == q then
      hit = true
      break
    end

  end
  
  unless hit
    puts k
    break
  end

end

Problem 47

Find the first four consecutive integers to have four distinct primes factors (each). What is the first of these numbers?
require 'primetool'
pt = PrimeTool.new(700)

factors = {}
k = 2*3*5*7
factors[k-1] = factors[k-2] = factors[k-3] = []

while true do

  factors[k] = []
  m = k

  pt.primes.each do |p|
    break if p > m

    if m % p == 0 then
      factors[k] << p
      m /= p while m%p == 0
      if factors[m]
        factors[k].concat factors[m]
        break
      end
      break if factors[k].length > 4
    end
  end

  if factors[k].length == 4 and factors[k-1].length == 4 and
      factors[k-2].length == 4 and factors[k-3].length == 4 then
    puts k-3
    break
  end

  k += 1

end

Problem 48

Find the last ten digits of the series, 11 + 22 + 33 + ... + 10001000.
puts [*1..1000].inject(0){ |s,i| s + ( (i**i) % (10**10) ) } % (10**10)
Idee: In jedem Schritt nur die letzten 10 Ziffern betrachten.

Problem 49

The arithmetic sequence, 1487, 4817, 8147, in which each of the terms increases by 3330, is unusual in two ways: (i) each of the three terms are prime, and, (ii) each of the 4-digit numbers are permutations of one another. (...) There is one other 4-digit increasing sequence. What 12-digit number do you form by concatenating the three terms in this sequence?
require 'primetool'
pt = PrimeTool.new(9999)

# 1009 ist die 169. Primzahl und die erste, die vierstellig ist
i = 169
j = i+1

while true do
  p = [ pt.prime(i), pt.prime(j), nil ]
  if p[1].nil? then
    i,j = i+1, i+2
    next
  end
  p[2] = 2 * p[1] - p[0]

  j += 1
  next unless pt.is_prime?(p[2])
  next unless p[0].to_s.split(//).sort == p[1].to_s.split(//).sort
  next unless p[0].to_s.split(//).sort == p[2].to_s.split(//).sort
  next unless p[1].to_s.split(//).sort == p[2].to_s.split(//).sort
  next if p[0] == 1487
  puts p.to_s
  break
end
Schöne Aufgabe!

Problem 50

Which prime, below one-million, can be written as the sum of the most consecutive primes?
require 'primetool'
n = 1000000
p = PrimeTool.new(n)
primes = p.primes

maxp = maxl = 0
for i in 0..(primes.length-1) do 

  sum = primes[i]
  list = [ sum ]

  for j in 1..((primes.length-1)-i) do

    sum += primes[i+j]
    break if sum >= n
    list << primes[i+j]

    if p.is_prime?(sum) then
      if list.length > maxl then
        maxp = sum
        maxl = list.length
      end
    end
  end
end

puts maxp

Problem 52

Find the smallest positive integer, x, such that 2x, 3x, 4x, 5x, and 6x, contain the same digits.
def digits(n) n.to_s.split(//).sort end

i=0
while true do
  i += 1
  digits = digits(i)
  next if digits(2*i) != digits
  next if digits(3*i) != digits
  next if digits(4*i) != digits
  next if digits(5*i) != digits
  next if digits(6*i) != digits
  puts i
  exit
end
Editor auf, Code runtergeschrieben, Editor zu, gestartet, tut.

Problem 53

How many, not necessarily distinct, values of nCr, for 1 <= n <= 100, are greater than one-million?
max = 100
count = 0
binomi = [ [1] ]

0.upto(max-1) do |n|
  binomi[n] = []
  0.upto(n) do |r|
    binomi[n][r] = (n==r) ? 1 : binomi[n][r] = binomi[n-1][r-1] + binomi[n-1][r]
    count += 1 if binomi[n][r] > 1000000
  end
end

puts count
Idee: Dynamische Programmierung und Rekursionsformel benutzen.

Problem 54

The file poker.txt contains one-thousand random hands dealt to two players. Each line of the file contains ten cards (separated by a single space): the first five are Player 1's cards and the last five are Player 2's cards. How many hands does Player 1 win?
def parsehand(s)
  s = s.split(/ /)
  s.each_index do |i|
    s[i] = s[i].split(//)
    s[i][0] = s[i][0].gsub("T","10").gsub("J","11").gsub("Q","12").gsub("K","13").gsub("A","14").to_i
    s = s.sort_by{ |a| a[0] }
  end
  s
end

def onepair?(h)
  return h[0][0] if h[0][0] == h[1][0] 
  return h[1][0] if h[1][0] == h[2][0]
  return h[2][0] if h[2][0] == h[3][0] 
  return h[3][0] if h[3][0] == h[4][0]
  return nil
end

def twopairs?(h)
  ( h[0][0] == h[1][0] and h[2][0] == h[3][0] ) or \
  ( h[0][0] == h[1][0] and h[3][0] == h[4][0] ) or \
  ( h[1][0] == h[2][0] and h[3][0] == h[4][0] )
end

def triplet?(h)
  ( h[0][0] == h[1][0] and h[1][0] == h[2][0] ) or \
  ( h[1][0] == h[2][0] and h[2][0] == h[3][0] ) or \
  ( h[2][0] == h[3][0] and h[3][0] == h[4][0] )
end

def straight?(h)
  h[0][0] + 1 == h[1][0] and \
  h[1][0] + 1 == h[2][0] and \
  h[2][0] + 1 == h[3][0] and \
  h[3][0] + 1 == h[4][0]
end

def flush?(h)
  h[0][1] == h[1][1] and h[1][1] == h[2][1] and h[2][1] == h[3][1] and h[3][1] == h[4][1]
end

def fullhouse?(h)
  ( h[0][0] == h[1][0] and h[1][0] == h[2][0] and h[3][0] == h[4][0] ) or \
  ( h[0][0] == h[1][0] and h[2][0] == h[3][0] and h[3][0] == h[4][0] )
end

def quadruplet?(h)
  ( h[0][0] == h[1][0] and h[1][0] == h[2][0] and h[2][0] == h[3][0] ) or \
  ( h[1][0] == h[2][0] and h[2][0] == h[3][0] and h[3][0] == h[4][0] )
end

def straightflush?(h)
  straight?(h) and flush?(h)
end

def royalflush?(h)
  straightflush?(h) and h[0][0] == 10
end

def rank(h)
  return 9 if royalflush?(h)
  return 8 if straightflush?(h)
  return 7 if quadruplet?(h)
  return 6 if fullhouse?(h)
  return 5 if flush?(h)
  return 4 if straight?(h)
  return 3 if triplet?(h)
  return 2 if twopairs?(h)
  return 1 if onepair?(h)
  return 0
end

def winner(g,h)

  if rank(g) < rank(h) then
    return 2
  elsif rank(g) > rank(h) then
    return 1
  end

  if rank(g) == 0 then
    return g[4][0] > h[4][0] ? 1 : 2
  end

  if rank(g) == 1 then
    if onepair?(g) > onepair?(h) then
      return 1
    elsif onepair?(g) < onepair?(h) then
      return 2
    else
      return g[4][0] > h[4][0] ? 1 : 0
    end
  end

end

puts File.open("54_poker.txt").readlines.map{ |l| [ l[0,14], l[15,14] ] }.select{ |h|
  winner(parsehand(h[0]), parsehand(h[1])) == 1
}.length
Lustige Aufgabe... Etwas lange Lösung.

Problem 55

If we take 47, reverse and add, 47 + 74 = 121, which is palindromic. A number that never forms a palindrome through the reverse and add process is called a Lychrel number. (...) every number below ten-thousand, it will either (i) become a palindrome in less than fifty iterations, or, (ii) no one, with all the computing power that exists, has managed so far to map it to a palindrome. How many Lychrel numbers are there below ten-thousand?
def lychrel?(n,i)
  return true if i == 50
  return false if n.to_s == n.to_s.reverse and i>0
  lychrel?(n + n.to_s.reverse.to_i, i+1)
end

puts (0..10**4).select{ |i| lychrel?(i,0) }.length

Problem 56

Considering natural numbers of the form, ab, where a, b < 100, what is the maximum digital sum?
n = 100

def qs(n)
  ret = 0
  while n > 0 do
    ret += n%10
    n/=10
  end
  return ret
end

max=0
(1..(n-1)).each{ |a| (1..(n-1)).each{ |b| max = [max,qs(a**b)].max } }
puts max

Problem 57

In the first one-thousand continued fraction expansions of sqrt(2), how many fractions contain a numerator with more digits than denominator?
n = 1000
c,a,b = 0,1,1
1.upto(n) do
  b, a = a+b, 2*b+a
  c+=1 if a.to_s.length > b.to_s.length
end
puts c
Etwas tricky drauf zu kommen, aber im Nachhinein relativ simpel.

Problem 58

What is the side length of the square spiral for which the ratio of primes along both diagonals first falls below 10%?
require 'primetool'
pt = PrimeTool.new(50000)

d = l = last = 1
primes = 0

while l += 2 do

  [ last + (l-1), last + 2*(l-1), last + 3*(l-1) ].each{ |n|
    primes += 1 if pt.is_prime_trial_with_sieve?(n)
  }

  last += 4*(l-1)
  d += 4
  break if 10*primes < d

end
puts l
Eigentlich eine leichte Aufgabe, aber trotzdem in Ruby recht langsam. Optimierungen, die mir eingefallen sind: Zahlenreihe durch Additionen statt Multiplikationen erzeugen; Letzte Zahl ist ein Quadrat (also nie prim); Nur ungerade Seitenlängen betrachten; ...

Problem 59

A (...) encryption method is to take a text file, (...), then XOR each byte with a given value, taken from a secret key. (...) the encryption key consists of three lower case characters. Using cipher1.txt (...), a file containing the encrypted ASCII codes, and the knowledge that the plain text must contain common English words, decrypt the message and find the sum of the ASCII values in the original text.
cipher = File.open("59_cipher1.txt").read.chomp.split(/,/).map{|c| c.to_i }
keydigs = [*"a"[0].."z"[0]]

keydigs.each do |a|
  keydigs.each do |b|
    keydigs.each do |c|

      key = [a,b,c]
      sum = 0

      catch :break do
        cipher.each_index do |i|
          sum += s = key[i % 3] ^ cipher[i]
          throw :break if s < 20 or (s >= 35 and s <= 38) or s > 122
        end
        puts sum
        exit
      end

    end
  end
end
Nettes Problem. Idee: Mögliche Keys bruteforcen und die Ciphers, deren zugehöriger Plaintext Sonderzeichen und nicht-darstellbare ASCII Zeichen enthält, ignorieren.

Problem 62

Find the smallest cube for which exactly five permutations of its digits are cube.
perms = 5
res = {}

digits = 1
i = 0
while i += 1 and c = i**3 do

  if c > 10**digits then
    break if ( r = res.values.select{ |v| v.length == perms }.map{ |v| v.min } ) != []
    digits += 1
  end

  nf = c.to_s.split("").sort.to_s
  res[nf] = res[nf].nil? ? [ c ] : (res[nf] << c).sort

end

puts r.min
Nicht die schönste Lösung, aber blitzschnell. Idee: Alle Kubikzahlen generieren und jedesmal wenn die Ziffernlänge sich erhöht hat, nachgucken ob wir bereits einen Treffer haben. Die Rechnungen werden in einem Hash gespeichert, dessen Key die "sortierte Zahl" ist (denn das ist offensichtlich eine Normalform für die Bahnen der Zahl unter Permutationen).

Problem 63

How many n-digit positive integers exist which are also an nth power?
i = j = 1
n = 0

while n += 1 and i < 10 do
  l = (i**n).to_s.length
  j += 1 if l == n
  i, n = i+1, 0 if l < n
end

puts j-1
Tipp: Man rechnet leicht nach, dass die Basis nicht größer als 9 sein darf.

Problem 65

Find the sum of digits in the numerator of the 100th convergent of the continued fraction for e.
require 'rational'
n = 100
exp = [ 2 ]
2.step(n,2){ |i| exp.concat [ 1, i, 1] }
def exp_to_rat(exp) Rational(exp[0],1) + ((exp.length == 1) ? 0 : Rational(1, exp_to_rat(exp[1..-1]))) end
puts exp_to_rat(exp[0..n-1]).numerator.to_s.split(//).inject(0){ |s,x| s + x.to_i }
Gähn. :-)

Problem 66

Consider quadratic Diophantine equations of the form: x2 - Dy2 = 1. Find the value of D <= 1000 in minimal solutions of x for which the largest value of x is obtained.
require 'rational'

def fu(s)

  m = [ 0 ]
  d = [ 1 ]
  a = [ Math.sqrt(s).to_i ]

  i = 0
  while i += 1 do

    m[i] = d[i-1] * a[i-1] - m[i-1]
    d[i] = (s - m[i] ** 2) / d[i-1]
    a[i] = (( a[0] + m[i] ) / d[i] ).to_i

    if i == 2 then
      lastlast = Rational(a[0],1)
      last = Rational(a[1] * a[0] + 1, a[1])
    end

    if i >= 2 then
      x = a[i] * last.numerator + lastlast.numerator
      y = a[i] * last.denominator + lastlast.denominator

      if x**2 - s * (y**2) == 1 then
        return Rational(x,y)
        break
      end

      lastlast = last
      last = Rational(x,y)

    end

  end

end

maxval = 0
maxi = 0

1.upto(1000) do |s|

  next if Math.sqrt(s).to_i ** 2 == s
  v = fu(s).denominator

  if v > maxval then
    maxval = v
    maxi = s
  end

end

puts maxi
Eine interessante Aufgabe mit viel Theorie dahinter, die mich einige Zeit gekostet hat. Glücklicherweise habe ich vor kurzem eine Vorlesung über algebraische Zahlentheorie gehört und habe daher erkannt, dass man hier die sogenannte Grundeinheiten im Ganzheitsring des quadratischen Zahlkörpers Q(sqrt(D)) bestimmen soll. Habe mich dann etwas schlau gemacht und herausgefunden, dass die Lösung des Problems in dem generieren der Kettenbruchapproximationen liegt (sqrt(D) ist irrational falls D kein Quadrat ist und hat daher eine unendliche Kettenbruchentwicklung). Ein paar Links als Anlaufpunkt: Infinite continued fractions, Continued fraction expansion of squares, Pell Gleichung.

Problem 67

Find the maximum total from top to bottom in triangle.txt, a 15K text file containing a triangle with one-hundred rows.
triangle = []
f = File.open("67_triangle.txt")
while l = f.gets do triangle << l.split(/ /) end
f.close
triangle.each_index{ |i| triangle[i].each_index{ |j| triangle[i][j] = triangle[i][j].to_i }}

(triangle.length-2).downto(0) { |i|
  for j in 0..(triangle[i].length-1) do
    triangle[i][j] += [triangle[i+1][j], triangle[i+1][j+1]].max
  end
}

puts triangle[0][0]
Siehe Problem 18.

Problem 69

Find the value of n <= 1,000,000 for which n/phi(n) is a maximum.
require 'primetool'
max = 10**6
n = 1
1.upto(max) do |p|
  next unless PrimeTool.is_prime_trial?(p)
  break if (n*p) >= max
  n *= p
end
puts n
Erst etwas Brute-Force probiert, dann gemerkt, dass man die Aufgabe recht einfach (sogar ohne Computer) lösen kann. Damit n/phi(n) gross wird, sollte phi(n) klein sein. Bekanntlich ist phi(p)=p-1 und phi zahlentheoretisch. Die korrekte Lösung ist daher phi(2*3*5*7*11*13*17) (Produkt der ersten k Primzahlen, so dass das Produkt kleiner als die gegebene Schranke ist).

Problem 71

By listing the set of reduced proper fractions n/d for d <= 1,000,000 in ascending order of size, find the numerator of the fraction immediately to the left of 3/7.
max = 10**6
a = nil

def ggT(a,b) (b == 0) ? a : ggT(b, a % b) end

max.downto(1) do |n|
  if (a = 3*n-1) % 7 == 0 then
    a /= 7
    break if ggT(a,n) == 1
  end
end

puts a
Lustigerweise habe ich mich genau in dem moment, in dem ich das Problem zum ersten mal gelesen habe, zufälligerweise mit genau diesem Thema beschäftigt (in der Vorlesung Algebraische Zahlentheorie II von Herrn Prof. Krieg an der RWTH-Aachen) und wusste daher, dass für zwei aufeinanderfolgende Farey-Brüche a/b und c/d die Beziehung bc-ad=1 gilt. Daher muss für den gesuchten Bruch a/b die Beziehung a = (3b-1)/7 gelten.

Problem 72

How many elements would be contained in the set of reduced proper fractions n/d for d <= 1,000,000?
require 'primetool'
max = 10**6
phi = Array.new(max+1){ |i| i }
pt = PrimeTool.new(max)

pt.primes.each do |i|
  phi[i] = i-1
  j=1
  while j += 1 and (k = i*j) <= max do
    phi[k] -= phi[k]/i
  end
end

puts phi[2..-1].inject(0){ |s,k| s+k }
Wenn man ein bisschen etwas über Farey-Brüche weiss, dann ist klar, dass hier die Summe über die Werte der Phi-Funktion gesucht ist. Habe zuerst ewig lange irgendwelche optimierten Brute-Force Varianten ausprobiert (die aber schon für deutlich kleinere Werte von d extrem langsam waren), bis ich zufällig im Netz eine klevere Methode gefunden habe, um eine Wertetabelle der Phi-Funktion aufzustellen (die ganz ähnlich funktioniert wie ein Primzahlsieb und viel schneller ist als der naive Ansatz über den euklidischen Algorithmus).

Problem 76

How many different ways can one hundred be written as a sum of at least two positive integers?
n = 100
@cache = Array.new(n+1)
@cache.each_index do |i| @cache[i] = [] end

def p(k,n)
  return 0 if k>n
  return 1 if k==n
  @cache[k][n] ? @cache[k][n] : ( @cache[k][n] = p(k+1,n) + p(k,n-k) )
end

puts p(1,n)-1
Jeder der schonmal etwas Kombinatorik betrieben hat weiss, dass es hier um die sogenannte Partitionsfunktion geht. Eine explizite Darstellung ist mir nicht bekannt, aber man kann sie relativ leicht rekursiv beschreiben und mit einer Memo Tabelle geht das sogar halbwegs flott. Links: Wolfram MathWorld, Sloans.

Problem 79

A common security method used for online banking is to ask the user for three random characters from a passcode. The text file, keylog.txt, contains fifty successful login attempts. Given that the three characters are always asked for in order, analyse the file so as to determine the shortest possible secret passcode of unknown length.
tsort = File.popen("tsort", "r+")
data = File.open("79_keylog.txt").readlines.sort.uniq.map do |v|
  v = v.split(//); tsort.puts "#{v[0]} #{v[1]} #{v[0]} #{v[2]} #{v[1]} #{v[2]}"
end
tsort.close_write
puts tsort.readlines.map{ |l| l.chomp }.join("")
An dieser Aufgabe bin ich lange verzweifelt bis mein Kollege Johannes die Aufgabe mit Stift und Papier in weniger als einer Minute gelöst hat. Das Prinzip ist natürlich total simpel, man stellt die Daten als gerichteten Graphen dar und macht eine Topologische Sortierung.

Problem 92

How many numbers below 10.000.000 are not happy?
@happymemo = []
@qsmemo = []
@happymemo[567] = nil
@happymemo[1] = true
@happymemo[89] = false

def qs(n)
  @qsmemo[n].nil? ? (  @qsmemo[n] = (n<10) ? n**2 : ( (n%10)**2 + qs(n/10) ) ) : @qsmemo[n]
end
 
def happy(n)
  (n>567) ? @happymemo[qs(n)] : ( @happymemo[n].nil? ? ( @happymemo[n] = happy(qs(n)) ) : @happymemo[n] )
end

puts [*2..10**7].select{ |n| !happy(n) }.length
Schöne Aufgabe eigentlich. Leider ist meine Lösung extrem langsam. Im wesentlichen Brute Force mit zwei Memo-Tables. Winzige Optimierung: Die Tabelle muss nur bis maximal 567 gehen, denn das ist die größtmögliche Zahl, die erreicht werden kann.

Problem 97

In 2004 there was found a massive non-Mersenne prime which contains 2,357,207 digits: 28433 * 27830457+1. Find the last ten digits of this prime number.
ret = 1
1.upto(783){ # 7830457 / 10000
  ret *= 2596709376 # (2^10000) % (10^10)
  ret %= (10**10)
}

ret *= (2**457) # 7830457 % 10000

puts ( 28433 * (ret % (10**10)) + 1 ) % ( 10 ** 10 )
Schöne Aufgabe! Idee: Große Schritte machen und in jedem schon nur mit den letzten 10 Stellen rechnen.

Problem 99

Using base_exp.txt, a 22K text file containing one thousand lines with a base/exponent pair on each line, determine which line number has the greatest numerical value.
f = File.open("99_base_exp.txt")
d = []
n = 0

while l = f.gets do
  d << [ n += 1, l.split(/,/).map{ |c| c.to_i } ].flatten
end

puts d.sort_by{ |n,a,b| (b * Math.log(a)) }.last[0]
Sehr einfach. Vergleiche die Logarithmen statt den Werten...

Problem 104

Given that Fk is the first Fibonacci number for which the first nine digits AND the last nine digits are 1-9 pandigital (contain all the digits 1 to 9, but not necessarily in order), find k.
def pandigital?(n)
  return false if n < 10**8
  return n.to_s.split(//).sort.map{ |i| i.to_i } == [*1..9] 
end

i,a,b,x,y = 1,1,1,1,1

while true do

  if pandigital?(a) and pandigital?(x.to_s[0..8].to_i) then
    puts i
    exit
  end

  a, b = b % (10**9), (a+b) % (10**9)
  x, y = y, x+y
  
  if y >= 10**18 then x /= 10; y /= 10 end

  i+=1

end
Eine recht schwere Aufgabe. Das gesuchte k ist relativ gross und die zugehörige k-te Fibonacci Zahl hat mehrere tausend Stellen. Idee: Erzeuge nicht die Fibonacci Folge sondern die Folge der letzten und vorderen 9 Ziffern jedes Folgengliedes.

Problem 119

The number 512 is interesting because it is equal to the sum of its digits raised to some power: 5 + 1 + 2 = 8, and 83 = 512. Another example of a number with this property is 614656 = 284. We shall define an to be the n-th term of this sequence and insist that a number must contain at least two digits to have a sum. You are given that a2 = 512 and a10 = 614656. Find a30.
def qs(n) n < 10 ? n : (n % 10) + qs(n/10) end

a = []

1.upto(100) do |b|
  a << [b, b]
  10.times do a << [b, a.last[1] * b] end
end

puts a.select{ |n| n[0] == qs(n[1]) }.select{ |n| n[1] >= 10 }.map{ |n| n[1] }.sort[29]
Erste Idee: Zahlen der Reihe nach durchlaufen und dann die passende Potenz dazu finden (viel zu langsam). Zweite Idee: Testen ob der Logarithmus zur Quersumme als Basis ganzzahlig ist (viel zu langsam). Dritte (gute) Idee: Alle Potenzen generieren und dann die rausfischen, die die gewünschte Eigenschaft haben (sehr schnell :-)).

Problem 120

Let r be the remainder when (a-1)n + (a+1)n is divided by a2. (...) as n varies, so too will r, but for a = 7 it turns out that rmax = 42. For 3 <= a <= 1000, find the sum of all the rmax.
puts [*3..1000].inject(0){ |s,a| s += 2 * (a - 2 + (a%2))/2 * a }
Recht einfach, wenn man Aufgabe 123 vorher schon gemacht hat.

Problem 123

Let pn be the n-th prime (...) and let r be the remainder when (pn-1)n + (pn+1)n is divided by pn2. Find the least value of n for which the remainder first exceeds 1010.
require 'primetool'
@pt = PrimeTool.new(5 * 10**5)
i = 7037
break if 2*i*@pt.primes[i-1] > 10**10 while i += 2
puts i
Nette Aufgabe. Mit dem Binomischen Lehrsatz lassen sich die beiden Potenzen ausschreiben, anschließend kann man zusammenfassen und man sieht, dass modulo pn2 fast alle Summanden gleich Null sind und sich der ganze Term zu 2 * n * pn vereinfacht, falls n ungerade ist, und zu 2, falls n gerade ist.

Problem 125

Find the sum of all the numbers less than 108 that are both palindromic and can be written as the sum of consecutive squares.
max = 10 ** 8
k = 0
hits = []

while k += 1 and n = k do

  while n += 1 do
    m = ( (n-k+1) * (2 * ( k*k + n*(k+n) ) + n-k) ) / 6
    break if m >= max
    hits << m if m.to_s == m.to_s.reverse
  end

  break if n == k+2

end

puts hits.uniq.inject(0){ |s,x| s+x }
Aus der bekannten Formel für die Summe der ersten n Quadrate, kann man leicht eine Formel ableiten für die Summe der Quadrate von k bis n. Vorsicht: Die Darstellung einer Zahl als Summe aufeinanderfolgender Quadrate ist nicht eindeutig (d.h. man muss aufpassen, dass man keine Zahl doppelt zählt).

Problem 131

There are some prime values, p, for which there exists a positive integer, n, such that the expression n3 + p * n2 is a perfect cube. How many primes below one million have this remarkable property?
require 'primetool'
n = 0
values = []
break if values.last > 10**6 while n+=1 and values << (n+1)**3 - n**3
puts values[0..-2].select{ |p| PrimeTool.is_prime_trial?(p) }.length
Sehr schöne Aufgabe. Der Brute-Force Ansatz lässt sich zwar etwas optimieren, aber dauert trotzdem noch mehrere Minuten. Schöner: n2 * (n + p) ist aufjedenfall eine Kubikzahl, wenn sowohl n als auch n+p welche sind (etwa n = m3). Wenn dies der Fall ist, ist p die Differenz zweier Kubikzahlen (p = (n+p) - n). Die Differenz zweier Kubikzahlen faktorisiert aber zu a3 - b3 = (a-b)(a2 + ab + b2), d.h. es muss a=b+1 gelten (also p = (m+1)3 - m3). Die Primzahl p ist also die Differenz zweier aufeinander folgender Kubikzahlen (solche Primzahlen nennt man übrigens Cuban Primes).

Problem 197

Given is the function f(x) = [230.403243784 - x2] * 10-9, the sequence un is defined by u0 = -1 and un+1 = f(un). Find un + un+1 for n = 1012. Give your answer with 9 digits after the decimal point.
def f(x) (2 ** (30.403243784 - (x*x))).floor / (10.0 ** 9) end
last = -1
1.upto(1000){ |n| last = f(last) }
puts last+f(last)
Sehr langweilige Aufgabe.. Wie viele andere vermutlich auch hatte ich eine Art von fraktalem und "chaotischem" Verhalten erwartet. Man sieht aber (durch ausprobieren) leicht, dass die Folge recht schnell (ab ca. 500 Iterationen) um zwei Werte oszilliert (und die Summe von aufeinanderfolgenden Folgengliedern damit konvergiert (und die ersten 9 Stellen sich nichtmehr ändern)).

Problem 203

Find the sum of the distinct squarefree numbers in the first 51 rows of Pascal's triangle.
rows = 51

def squarefree?(n)
  d = 1
  while d += 1 do
    return true if d > n
    if n % d == 0 then
      n /= d
      return false if n%d == 0
    end
  end
end

binomi = [ [1] ]
0.upto(rows-1) do |n|
  binomi[n] = []
  0.upto(n) do |r|
    binomi[n][r] = (n==r || r == 0) ? 1 : ( binomi[n-1][r-1] + binomi[n-1][r] )
  end
end

puts binomi.flatten.uniq.select{ |k| squarefree?(k) }.inject(0){ |s,k| s+k }
Viel zu einfach für ein Level 5 Problem.

Problem 206

Find the unique positive integer whose square has the form 1_2_3_4_5_6_7_8_9_0, where each "_" is a single digit.
max = Math.sqrt(192939495969798990 / 10).ceil
while (max*max).to_s !~ /^1\d2\d3\d4\d5\d6\d7\d8\d9$/ do max -= 2 end
puts max*10
Schöne Aufgabe, einfache Lösung. Die Zahl endet mit 0, ist also durch 10 teilbar. Da es sich um ein Quadrat handelt (und 10 quadratfrei ist) muss auch die Wurzel durch 10 teilbar sein, also die Zahl selbst sogar durch 100, d.h. auch die vorletzte Ziffer muss eine 0 sein, die letzten zwei Ziffern können also ignoriert werden, damit ist die letzte relevante Ziffer eine 9. Eine Zahl, deren Quadrat als letzte Ziffer eine 9 hat, muss als letzte Ziffer eine 3 oder eine 7 haben (sieht man an der Rechnung modulo 10), in jedem Fall ist unsere gesuchte Wurzel also ungerade. Die größtmögliche Wurzel ist also 138902663. Von hier aus testen wir abwärts in Zweierschritten, ob wir eine Lösung gefunden haben (und multiplizieren diese anschließend mit 10).

Tools

Um oft auftauchenden Code leicht wiederverwenden zu können, habe ich einige Teile in extra Dateien ausgelagert.

primetool.rb

Ein primitives Primzahlsieb.
class PrimeTool
  
  def initialize(n)

    @primes  = []
    @marked = [*0..n]
    @marked[0] = @marked[1] = nil
    limit = Math.sqrt(n).floor

    @marked.each do |i|
      next unless i
      break if i > limit
      (i*i).step(n,i) { |m| @marked[m] = nil }
    end

    @primes = @marked.select{ |p| p != nil }

    return self
  end

  def is_prime?(n)
    return ( @marked[n] != nil )
  end

  def self.is_prime_trial?(n)

    return (n==2) if (n%2==0)
    
    d = 3
    while d*d <= n do
      return false if (n%d==0)
      d += 2
    end

    return true

  end

  def is_prime_trial_with_sieve?(n)
    return is_prime?(n) if n < @primes.last
    primes.each do |p|
      return false if n%p == 0
    end
    return true
  end

  def prime(i)
    return @primes[i-1]
  end

  def primes
    return @primes
  end
end
Valid CSS! Valid XHTML 1.0 Transistional! Hacker