Solves problem 59

This commit is contained in:
Julien Lengrand-Lambert
2014-05-25 11:39:57 +02:00
parent d806de2264
commit d6a04fbe42
7 changed files with 356 additions and 24 deletions

View File

@@ -71,11 +71,14 @@ So you may find some of the code here quite ugly. And this is the case :). Why o
54 - How many hands does Player 1 win in radndom hands of Poker? - < 1 sec <br /> 54 - How many hands does Player 1 win in radndom hands of Poker? - < 1 sec <br />
55 - How many Lychrel numbers are there below ten-thousand? - < 1 sec <br /> 55 - How many Lychrel numbers are there below ten-thousand? - < 1 sec <br />
56 - Considering natural numbers of the form, a^b, finding the maximum digital sum. > 3 sec <br /> 56 - Considering natural numbers of the form, a^b, finding the maximum digital sum. > 3 sec <br />
59 - Decrypt XOR ASCII - 23.4s<br />
67 - Using an efficient algorithm find the maximal sum in the triangle? - 0.027 <br /> 67 - Using an efficient algorithm find the maximal sum in the triangle? - 0.027 <br />
## In progress: ## In progress:
47 - Find the first four consecutive integers to have four distinct primes factors. <br /> 47 - Find the first four consecutive integers to have four distinct primes factors. <br />
57 - How many fractions contain a numerator with more digits than denominator? <br />
58 - What is the side length of the square spiral for which the ratio of primes along both diagonals first falls below 10%? <br />
97 - Find the last ten digits of the non-Mersenne prime: 28433 <20> 2^7830457 + 1. <br /> 97 - Find the last ten digits of the non-Mersenne prime: 28433 <20> 2^7830457 + 1. <br />
## Contact ## Contact
@@ -87,4 +90,4 @@ You can contact me at julien at lengrand dot fr, or on my [current website](http
![Current Project Euler status](http://projecteuler.net/profile/jlengrand.png) ![Current Project Euler status](http://projecteuler.net/profile/jlengrand.png)
Last update : 22/05/2014 Last update : 25/05/2014

View File

@@ -87,12 +87,4 @@ def findPrimePerms(prime):
if __name__ == '__main__': if __name__ == '__main__':
primes = fourLengthPrime() primes = fourLengthPrime()
print getPrimesPermutations(primes, 3) print getPrimesPermutations(primes, 3)
#itertools.combinations(lst, i)]
#getPrimesPermutations(primes, 3)
# try:
# while primes.next():
# print primes.next()
# except StopIteration:
# print "Reached end of list"
#print "Answer : %d " % (last_ten()) #print "Answer : %d " % (last_ten())

30
e_57.py Normal file
View File

@@ -0,0 +1,30 @@
#!/usr/bin/env python
'''
Created on 10 feb. 2012
@author: Julien Lengrand-Lambert
DESCRIPTION: Solves problem 57 of Project Euler
It is possible to show that the square root of two can be expressed
as an infinite continued fraction.
sqrt 2 = 1 + 1/(2 + 1/(2 + 1/(2 + ... ))) = 1.414213...
By expanding this for the first four iterations, we get:
1 + 1/2 = 3/2 = 1.5
1 + 1/(2 + 1/2) = 7/5 = 1.4
1 + 1/(2 + 1/(2 + 1/2)) = 17/12 = 1.41666...
1 + 1/(2 + 1/(2 + 1/(2 + 1/2))) = 41/29 = 1.41379...
The next three expansions are 99/70, 239/169, and 577/408,
but the eighth expansion, 1393/985, is the first example where the number
of digits in the numerator exceeds the number of digits in the denominator.
In the first one-thousand expansions, how many fractions contain a numerator
with more digits than denominator?
'''
if __name__ == '__main__':
print "plouf"
#print "Answer : %d " % (last_ten())

31
e_58.py Normal file
View File

@@ -0,0 +1,31 @@
#!/usr/bin/env python
'''
Created on 10 feb. 2012
@author: Julien Lengrand-Lambert
DESCRIPTION: Solves problem 58 of Project Euler
Starting with 1 and spiralling anticlockwise in the following way,
a square spiral with side length 7 is formed.
37 36 35 34 33 32 31
38 17 16 15 14 13 30
39 18 5 4 3 12 29
40 19 6 1 2 11 28
41 20 7 8 9 10 27
42 21 22 23 24 25 26
43 44 45 46 47 48 49
It is interesting to note that the odd squares lie along the bottom right diagonal,
but what is more interesting is that 8 out of the 13 numbers lying along both
diagonals are prime; that is, a ratio of 8/13 ≈ 62%.
If one complete new layer is wrapped around the spiral above, a square spiral
with side length 9 will be formed. If this process is continued,
what is the side length of the square spiral for which the ratio of primes
along both diagonals first falls below 10%?
'''
if __name__ == '__main__':
print "plouf"
#print "Answer : %d " % (last_ten())

255
e_59.py Normal file
View File

@@ -0,0 +1,255 @@
#!/usr/bin/env python
'''
Created on 10 feb. 2012
@author: Julien Lengrand-Lambert
DESCRIPTION: Solves problem 59 of Project Euler
Each character on a computer is assigned a unique code
and the preferred standard is ASCII (American Standard Code for Information Interchange).
For example, uppercase A = 65, asterisk (*) = 42, and lowercase k = 107.
A modern encryption method is to take a text file, convert the bytes to ASCII,
then XOR each byte with a given value, taken from a secret key.
The advantage with the XOR function is that using the same encryption key
on the cipher text, restores the plain text; for example, 65 XOR 42 = 107,
then 107 XOR 42 = 65.
For unbreakable encryption, the key is the same length as the plain text message,
and the key is made up of random bytes.
The user would keep the encrypted message and the encryption key in different locations,
and without both "halves", it is impossible to decrypt the message.
Unfortunately, this method is impractical for most users,
so the modified method is to use a password as a key. If the password is
shorter than the message, which is likely, the key is repeated cyclically
throughout the message. The balance for this method is using a sufficiently
long password key for security, but short enough to be memorable.
Your task has been made easy, as the encryption key consists of three lower
case characters. Using cipher1.txt (right click and 'Save Link/Target As...'),
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.
'''
import itertools
import string
base = 16 # hey, its hex!
printable_letters = string.printable # list of all printable characters
#frequency of english letters
en_freq = {'a': 0.08167,
'b': 0.01492,
'c':0.02782,
'd':0.04253,
'e':0.12702,
'f':0.02228,
'g':0.02015,
'h':0.06094,
'i':0.06966,
'j':0.00153,
'k':0.00772,
'l':0.04025,
'm':0.02406,
'n':0.06749,
'o':0.07507,
'p':0.01929,
'q':0.00095,
'r':0.05987,
's':0.06327,
't':0.09056,
'u':0.02758,
'v':0.00978,
'w':0.02360,
'x':0.00150,
'y':0.01974,
'z':0.00074}
def _xorhex(a, b):
"""
XORs a against b and returns the result
a and b are expected to be int values
XOR Truth Table
Input Output
A B
0 0 0
0 1 1
1 0 1
1 1 0
"""
long_res = a ^ b
string_res = '%x' % long_res # returns a string version
return long_res
def load_data(filename):
"Loads the data of the filename into a table"
file = open(filename, "r")
stuff = file.read()
return [int(val) for val in stuff.split(',')]
def all_passwords(range, length):
'''
Returns all possible passwords given a range of ascii_values, and the length of the password
'''
return list(set(itertools.combinations(range*3, length)))
def apply_password(value, password):
"""
Applies a given password to the message.
"""
long_pass = repeat_password(password, len(value))
return [_xorhex(a, b) for a, b in zip(value, long_pass)]
def repeat_password(password, length):
"""
Repeats a password to match the requested size.
Ex : [1, 2, 3], 7 => [1, 2, 3, 1, 2, 3, 1]
"""
repeat = length / len(password)
rest = length % len(password)
return password * repeat + password[:rest]
def all_solutions(data, pass_range, length):
"""
Prints all possible solutions
"""
res = []
all_pass = all_passwords(pass_range, length)
total = len(all_pass)
print total
idx = 0
for password in all_pass:
idx += 1
print "%d / %d" % (idx, total)
sol = apply_password(data, password)
res.append([to_ascii(sol), password])
return res
def to_ascii(data):
"""
Turns a list of ascii values into a proper string
"""
res = ""
for el in data:
res += chr(el)
return res
def discard_non_printable(solutions):
"""
We discard from a list of hex all the elements that cannot be printed.
#FIXME: Do better
"""
res = []
for val in solutions:
is_printable = True
for v in val[0]:
if not(v in printable_letters):
is_printable = False
if is_printable:
res.append(val)
return res
def letter_freq(work_val):
"""
Calculate the frequency of the elements in the string val
Returns a dictionary of the form {element: frequency} for all elements of val.
Only present elements will be present in the dictionary (frequency > 0)
"""
counts = map(work_val.count ,work_val) # gets count of each element of hex_val
freqs = [(float(x) / len(work_val)) for x in counts]
#freqs = [x for x in counts]
return dict(zip(work_val,freqs)) # transforms into dict of unique values associated to frequencies
def chi2(ref_dict, test_dict, length):
"""
Performs a chi2 adequation testing.
Given a reference dictionary and a corresponding dctionary to be tested, returns a score corresponding to
the similitude between the test data and the reference data.
The lower the score, the higher the similitude.
Dictionaries are expected to be under the {value:frequency} form.
Example : chi2({a:0.08, b:0.92}, {a:0.80, b:0.20}, 12)
length corresponds to the number of elements needed to acquire test_dict
Returns a float value
"""
my_keys = ref_dict.keys()
score = 0
for m in my_keys:
if test_dict.has_key(m): # testing if occurence happened
freq_val = length * test_dict[m]
else:
freq_val = 0
ref_val = length * ref_dict[m]
score += pow(freq_val - ref_val, 2) / ref_val
return score
def strip_string_to_lowercase(s):
"""
Returns s in lowercase, with all its special characters stripped
Taken from here: http://stackoverflow.com/questions/638893/what-is-the-most-efficient-way-in-python-to-convert-a-string-to-all-lowercase-st#639325
"""
tmpStr = s.lower().strip()
retStrList = []
for x in tmpStr:
if x in string.ascii_lowercase:
retStrList.append(x)
return ''.join(retStrList)
def close_to_english(element):
"""
Returns a score of likeliness for element to be english
The computed score is the chi2 adequation test.
The closest to 0, the most probable the sentence is to be in english
Table can be found here, solutions at row 25 (english is 26 degrees of liberty).
http://www.medcalc.org/manual/chi-square-table.php to get the values to compare to
"""
# cleans the hex val of special characters, and puts to lowercase
stripped_el = strip_string_to_lowercase(element)
length_el = len(stripped_el)
#calculates the letters frequency of element
freqs = letter_freq(stripped_el)
return chi2(en_freq, freqs, length_el)
def find_most_probable(data, pass_range, length):
solutions = all_solutions(data, pass_range, length)
# discards easy fuck ups
solutions = discard_non_printable(solutions)
best_candidate = ""
best_chi = 1000000
for sol in solutions:
score = close_to_english(sol[0])
if score < best_chi:
best_chi = score
best_candidate = sol
return best_candidate
def get_solution(data, pass_range, length):
solution = find_most_probable(data, ascii_range, 3)[0]
sum = 0
for val in solution:
sum+=ord(val)
return sum
if __name__ == '__main__':
data = load_data("e_59_cipher1.txt")
ascii_range = range(ord('a'), ord('z') + 1 )
print get_solution(data, ascii_range, 3)

1
e_59_cipher1.txt Normal file
View File

@@ -0,0 +1 @@
79,59,12,2,79,35,8,28,20,2,3,68,8,9,68,45,0,12,9,67,68,4,7,5,23,27,1,21,79,85,78,79,85,71,38,10,71,27,12,2,79,6,2,8,13,9,1,13,9,8,68,19,7,1,71,56,11,21,11,68,6,3,22,2,14,0,30,79,1,31,6,23,19,10,0,73,79,44,2,79,19,6,28,68,16,6,16,15,79,35,8,11,72,71,14,10,3,79,12,2,79,19,6,28,68,32,0,0,73,79,86,71,39,1,71,24,5,20,79,13,9,79,16,15,10,68,5,10,3,14,1,10,14,1,3,71,24,13,19,7,68,32,0,0,73,79,87,71,39,1,71,12,22,2,14,16,2,11,68,2,25,1,21,22,16,15,6,10,0,79,16,15,10,22,2,79,13,20,65,68,41,0,16,15,6,10,0,79,1,31,6,23,19,28,68,19,7,5,19,79,12,2,79,0,14,11,10,64,27,68,10,14,15,2,65,68,83,79,40,14,9,1,71,6,16,20,10,8,1,79,19,6,28,68,14,1,68,15,6,9,75,79,5,9,11,68,19,7,13,20,79,8,14,9,1,71,8,13,17,10,23,71,3,13,0,7,16,71,27,11,71,10,18,2,29,29,8,1,1,73,79,81,71,59,12,2,79,8,14,8,12,19,79,23,15,6,10,2,28,68,19,7,22,8,26,3,15,79,16,15,10,68,3,14,22,12,1,1,20,28,72,71,14,10,3,79,16,15,10,68,3,14,22,12,1,1,20,28,68,4,14,10,71,1,1,17,10,22,71,10,28,19,6,10,0,26,13,20,7,68,14,27,74,71,89,68,32,0,0,71,28,1,9,27,68,45,0,12,9,79,16,15,10,68,37,14,20,19,6,23,19,79,83,71,27,11,71,27,1,11,3,68,2,25,1,21,22,11,9,10,68,6,13,11,18,27,68,19,7,1,71,3,13,0,7,16,71,28,11,71,27,12,6,27,68,2,25,1,21,22,11,9,10,68,10,6,3,15,27,68,5,10,8,14,10,18,2,79,6,2,12,5,18,28,1,71,0,2,71,7,13,20,79,16,2,28,16,14,2,11,9,22,74,71,87,68,45,0,12,9,79,12,14,2,23,2,3,2,71,24,5,20,79,10,8,27,68,19,7,1,71,3,13,0,7,16,92,79,12,2,79,19,6,28,68,8,1,8,30,79,5,71,24,13,19,1,1,20,28,68,19,0,68,19,7,1,71,3,13,0,7,16,73,79,93,71,59,12,2,79,11,9,10,68,16,7,11,71,6,23,71,27,12,2,79,16,21,26,1,71,3,13,0,7,16,75,79,19,15,0,68,0,6,18,2,28,68,11,6,3,15,27,68,19,0,68,2,25,1,21,22,11,9,10,72,71,24,5,20,79,3,8,6,10,0,79,16,8,79,7,8,2,1,71,6,10,19,0,68,19,7,1,71,24,11,21,3,0,73,79,85,87,79,38,18,27,68,6,3,16,15,0,17,0,7,68,19,7,1,71,24,11,21,3,0,71,24,5,20,79,9,6,11,1,71,27,12,21,0,17,0,7,68,15,6,9,75,79,16,15,10,68,16,0,22,11,11,68,3,6,0,9,72,16,71,29,1,4,0,3,9,6,30,2,79,12,14,2,68,16,7,1,9,79,12,2,79,7,6,2,1,73,79,85,86,79,33,17,10,10,71,6,10,71,7,13,20,79,11,16,1,68,11,14,10,3,79,5,9,11,68,6,2,11,9,8,68,15,6,23,71,0,19,9,79,20,2,0,20,11,10,72,71,7,1,71,24,5,20,79,10,8,27,68,6,12,7,2,31,16,2,11,74,71,94,86,71,45,17,19,79,16,8,79,5,11,3,68,16,7,11,71,13,1,11,6,1,17,10,0,71,7,13,10,79,5,9,11,68,6,12,7,2,31,16,2,11,68,15,6,9,75,79,12,2,79,3,6,25,1,71,27,12,2,79,22,14,8,12,19,79,16,8,79,6,2,12,11,10,10,68,4,7,13,11,11,22,2,1,68,8,9,68,32,0,0,73,79,85,84,79,48,15,10,29,71,14,22,2,79,22,2,13,11,21,1,69,71,59,12,14,28,68,14,28,68,9,0,16,71,14,68,23,7,29,20,6,7,6,3,68,5,6,22,19,7,68,21,10,23,18,3,16,14,1,3,71,9,22,8,2,68,15,26,9,6,1,68,23,14,23,20,6,11,9,79,11,21,79,20,11,14,10,75,79,16,15,6,23,71,29,1,5,6,22,19,7,68,4,0,9,2,28,68,1,29,11,10,79,35,8,11,74,86,91,68,52,0,68,19,7,1,71,56,11,21,11,68,5,10,7,6,2,1,71,7,17,10,14,10,71,14,10,3,79,8,14,25,1,3,79,12,2,29,1,71,0,10,71,10,5,21,27,12,71,14,9,8,1,3,71,26,23,73,79,44,2,79,19,6,28,68,1,26,8,11,79,11,1,79,17,9,9,5,14,3,13,9,8,68,11,0,18,2,79,5,9,11,68,1,14,13,19,7,2,18,3,10,2,28,23,73,79,37,9,11,68,16,10,68,15,14,18,2,79,23,2,10,10,71,7,13,20,79,3,11,0,22,30,67,68,19,7,1,71,8,8,8,29,29,71,0,2,71,27,12,2,79,11,9,3,29,71,60,11,9,79,11,1,79,16,15,10,68,33,14,16,15,10,22,73

View File

@@ -3,9 +3,33 @@
{ {
"selected_items": "selected_items":
[ [
[
"sol",
"solution"
],
[
"all_",
"all_pass"
],
[
"asci",
"ascii_range"
],
[ [
"all", "all",
"all_permutations" "all_passwords"
],
[
"find",
"find_most_probable"
],
[
"all_p",
"all_pass"
],
[
"rep",
"repeat_password"
], ],
[ [
"ele", "ele",
@@ -35,10 +59,6 @@
"ee", "ee",
"election_id" "election_id"
], ],
[
"find",
"find_by_uuid"
],
[ [
"ca", "ca",
"candidates" "candidates"
@@ -477,6 +497,14 @@
}, },
"file_history": "file_history":
[ [
"/home/jll/Documents/01_perso/03_project_euler/e_59.py",
"/home/jll/Documents/01_perso/03_project_euler/README.markdown",
"/home/jll/Documents/01_perso/03_project_euler/e_59_cipher1.txt",
"/home/jll/Documents/01_perso/03_project_euler/e_18.py",
"/home/jll/Documents/01_perso/03_project_euler/e_13.data",
"/home/jll/Documents/01_perso/03_project_euler/e_18.data",
"/home/jll/Documents/01_perso/03_project_euler/e_22.data",
"/home/jll/Documents/01_perso/03_project_euler/e_22.py",
"/home/jll/Documents/01_perso/03_project_euler/e_47.py", "/home/jll/Documents/01_perso/03_project_euler/e_47.py",
"/home/jll/Documents/01_perso/00_myelections/app/controllers/application_controller.rb", "/home/jll/Documents/01_perso/00_myelections/app/controllers/application_controller.rb",
"/home/jll/Documents/01_perso/00_myelections/app/views/new_election_admin_mailer/new_election_admin_email.text.erb", "/home/jll/Documents/01_perso/00_myelections/app/views/new_election_admin_mailer/new_election_admin_email.text.erb",
@@ -596,15 +624,7 @@
"/home/jll/Documents/01_perso/18_blog/welcome-in-my-world/index.html", "/home/jll/Documents/01_perso/18_blog/welcome-in-my-world/index.html",
"/home/jll/Documents/01_perso/18_blog/posts.md", "/home/jll/Documents/01_perso/18_blog/posts.md",
"/home/jll/Documents/01_perso/18_blog/404.md", "/home/jll/Documents/01_perso/18_blog/404.md",
"/home/jll/Documents/01_perso/18_blog/_config.yml", "/home/jll/Documents/01_perso/18_blog/_config.yml"
"/home/jll/Documents/01_perso/18_blog/_posts/2012-03-05-computer-vision-companies.markdown",
"/home/jll/Documents/01_perso/18_blog/_posts/2011-10-17-17.markdown",
"/home/jll/Documents/01_perso/18_blog/_posts/2011-10-17-home.markdown",
"/home/jll/Documents/01_perso/18_blog/_posts/2011-10-13-job-space.markdown",
"/home/jll/Documents/01_perso/18_blog/_posts/2012-09-07-ivolution-development-status-36.markdown",
"/home/jll/Documents/01_perso/18_blog/_posts/2012-06-11-remove-all-your-thumbs-files.markdown",
"/home/jll/Documents/01_perso/18_blog/_posts/2012-06-04-is-descartes-the-father-of-agile-development.markdown",
"/home/jll/Documents/01_perso/18_blog/_posts/2012-02-22-computer-vision-companies.markdown"
], ],
"find": "find":
{ {
@@ -821,7 +841,7 @@
}, },
"output.exec": "output.exec":
{ {
"height": 236.0 "height": 253.0
}, },
"output.jenkinsIndicator": "output.jenkinsIndicator":
{ {