I'm working on problem 22 of Project Euler:
Using names.txt (right click and 'Save Link/Target As...'), a 46K 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?
http://projecteuler.net/problem=22
When I compile my code below, I get the answer 871196077. The correct answer should be 871198282.
import time
def euler_22():
## Creates a sorted list of the names in Py_Euler_22.txt
names = open('Py_Euler_22.txt', 'r')
names = names.read()
names = names.split('","')
names[0] = names[0][1:]
names[-1] = names[-1][:-2]
names = sorted(names)
## Creates a dictionary: letter -> value
value_letters = {}
start = ord("A")
for i in range(0, 26):
value_letters[chr(start+i)] = i+1
result = 0
for i in range(1, len(names)+1):
name = names[i-1]
sum_letters = 0
for letter in name:
sum_letters += value_letters[letter]*i
# = value of the letter multiplied with the name position
result += sum_letters
return result
tstart = time.time()
print euler_22()
print "Run time: " + str(time.time() - tstart)
I tried to find a program with a similar solution, but I only know Python, that limits the options.
I ran the program with simpler text-files, I created, where I can get the answer without a program and all of them worked. I googled the answer to the problem, but that didn't help either, since I cant find the missing points.
I'm a beginner, so I would really appreciate any tips regarding the program and Python, not only those, that will help me to solve the problem correctly.
Thanks a lot!
You have accidentally mangled one name.
Here qnames is the sorted list of names your code produces, and sorted_names is mine:
>>> for a,b in zip(qnames, sorted_names):
... if a != b:
... print a, b
...
ALONS ALONSO
For fun: a one-liner - nested list comprehensions, avast ye!
print sum ( [ (pos+1) * nv for pos, nv in enumerate([ sum ( [ ord(char) - 64 for char in name ] ) for name in sorted([name.strip('"') for name in open('names.txt','r').readline().split(",")]) ]) ] )
Or more readably:
print sum (
[(pos+1) * nv for pos, nv in
enumerate([ sum ([ ord(char) - 64 for char in name ] ) for name in
sorted([name.strip('"') for name in
open('names.txt','r').readline().split(",")]) ]) ] )
The black magic is that ASCII A is integer 65, ASCII B is integer 66, and so on - so ord(char) - 64 gets you the "letter value" of char.
Edit 2:
The full, human-readable, solution that I crammed into one line for your amusement.
with open('names.txt','r') as f:
data = f.readline();
names = [name.strip('"') for name in data.split(",")]
sorted_names = sorted(names)
name_values = [ sum ( [ ord(char) - 64 for char in name ] ) for name in sorted_names ]
name_position_values = [ (pos+1) * nv for pos, nv in enumerate(name_values) ]
total_sum = sum(name_position_values)
# debug output
from pprint import pprint
#position, word value, position * word value, word
pprint(zip(xrange(1,len(names)+1),name_values,name_position_values,sorted_names))
Note the heavy use of list comprehensions [x for x in list_of_xes] instead of loops, and the sum() function instead of for x in xes: sum += x.
There are some other tricks in here, but the take-home lesson is that list comprehensions and functions that process lists can make your code much simpler and easier to read.
Edit 3:
The pprint.pprint() function is a "pretty print()". It's great for debugging.
Edit 4:
Code golf version (142 chars):
print sum([(p+1)*v for p,v in enumerate([sum(map(ord,n))-64*len(n) for n in sorted([n[1:-1] for n in open('names.txt').read().split(",")])])])
I just cross-checked your code, and it looks like you're inadvertently chopping off the last character of the last word. To strip off the quotes from the last word, use:
names[-1] = names[-1][:-1]
Rather than trying to strip all the quotes from the names at once when you're converting the file contents to a list, strip them when you're processing the list.
# Project Euler Problem 22
# Name Scores
def score(name):
total = 0
for char in name:
total += (ord(char) - 64) # scale so A = 1, B = 2...
return total
def main():
# Open the names file for reading
infile = open('names.txt', 'r')
# Read the entire contents of the file
file_contents = infile.read()
# Close the file
infile.close()
# Convert file contents to a list of quoted names and sort them
list_of_names = file_contents.split(',')
list_of_names.sort()
position = 1
total = 0
for name in list_of_names:
name = name.strip('"') # strip the quotes from names individually
total += score(name) * position
position += 1
print(total)
if __name__ == "__main__":
main()
Related
I have a list of phone numbers and these need to be written in a certain way.
As for now they're listed as "+3212345678" and I wish the add spaces in between characters after certain amounts of numbers.
Result should be "+32 1 234 56 78"
You can use the format method with unpacking to provide it with each individual character as arguments. This will let you control the separators and get fancy formatting capabilities:
sep = "({}{}{}) {} {}{}{}.{}{}.{}{}" # {} are placeholders for digits
t = "+3212345678"
f = sep.format(*t)
print(f)
(+32) 1 234.56.78
You could extend this to using a dictionary for different formats depending on the length of the phone number (or other attributes):
seps = { 6:"{}{}.{}{}.{}{}",
7:"{}{}{}.{}{}.{}{}",
8:"{}{} {}{}.{}{}.{}{}",
10:"({}{}) {} {}{}{}.{}{}.{}{}",
11:"({}{}{}) {} {}{}{}.{}{}.{}{}" }
t = "+3212345678"
f = seps[len(t)].format(*t)
print(f)
"(+32) 1 234.56.78"
t = "44345678"
f = seps[len(t)].format(*t)
print(f)
"44 34.56.78"
Try the following in python
string="+3212345678"
n=3
string=string[0:n]+" "+string[n:]
string
'+32 12345678'
You can make a list which stores the number of characters space_list before a space, and add each character to a new list in a nested loop based on your space_list which adds a space right after the inner loop.
def format_num(space_list: list):
fmt_num = ""
count = 0
for space in space_list:
for s in range(space):
fmt_num += phn_num[count]
count += 1
fmt_num += " "
return fmt_num
phn_num = "+3212345678"
spaces = [3, 1, 3, 2, 2]
print(format_num(spaces))
Is there way to split or chunk the dynamic string into fixed size? let me explain:
Suppose:
name = Natalie
Family = David12
length = len(name) #7 bit
length = len(Family) # 7 bit
i want to split the name and family into and merging as :
result=nadatavilid1e2
and again split and extract the the 2 string as
x= Natalie
y= david
another Example:
Name = john
Family= mark
split and merging:
result= jomahnrk
and again split and extract the the 2 string as
x=john
y= mark
.
Remember variable name and family have different size length every time not static! . i hope my question is clear. i have seen some related solution about it like here and here and here and here and here and here and here but none of these work with what im looking for. Any suggestion ?? Thanks
i'm using spyder python 3.6.4
I have try this code split data into two parts:
def split(data):
indices = list(int(x) for x in data[-1:])
data = data[:-1]
rv = []
for i in indices[::-1]:
rv.append(data[-i:])
data=data[:-i]
rv.append(data)
return rv[::-1]
data='Natalie'
x,c=split(str(data))
print (x)
print (c)
Given you have stated names will always be of equal length you could use wrap to split in to 2 char pairs and the zip and chain to join them up. In the split part you can again use wwrap to split in 2 char pairs but if the number of pairs is odd then you need to split the last pair into 2 single entries. something like.
from textwrap import wrap
from itertools import chain
def merge_names(name, family):
name_split = wrap(name, 2)
family_split = wrap(family, 2)
return "".join(chain(*zip(name_split, family_split)))
def split_names(merged_name):
names = ["", ""]
char_pairs = wrap(merged_name, 2)
if len(char_pairs) % 2:
char_pairs.append(char_pairs[-1][1])
char_pairs[-2] = char_pairs[-2][0]
for index, chars in enumerate(char_pairs):
pos = 1 if index % 2 else 0
names[pos] += chars
return names
print(merge_names("john", "mark"))
print(split_names("jomahnrk"))
print(merge_names("stephen", "natalie"))
print(split_names("stnaeptaheline"))
print(merge_names("Natalie", "David12"))
print(split_names("NaDatavilid1e2"))
OUTPUT
jomahnrk
['john', 'mark']
stnaeptaheline
['stephen', 'natalie']
NaDatavilid1e2
['Natalie', 'David12']
Something like:
a = "Eleonora"
b = "James"
l = max(len(a), len(b))
a = a.lower() + " " * (l-len(a))
b = b.lower() + " " * (l-len(b))
n = 2
a = [a[i:i+n] for i in range(0, len(a), n)]
b = [b[i:i+n] for i in range(0, len(b), n)]
ans = "".join(map(lambda xy: "".join(xy), zip(a, b))).replace(" ", "")
Giving for this example:
eljaeomenosra
I have a exercise ,Input data will contain the total count of pairs to process in the first line.
The following lines will contain pairs themselves - one pair at each line.
Answer should contain the results separated by spaces.
My code:
n = int(raw_input())
sum = 0
for i in range(n):
y = raw_input().split(" ")
for i in y:
sum = sum + int(i)
print sum
With my code , I come the Sum together, but I will that the results to come separated by spaces . Thanks for yours help .
with your current code what you get is the total sum of all the given numbers, to get the sum per line you need to initialize your counter in the outer loop, and then print it, and as you want to print all it in the same line there are several ways to do it, like save it in a list or telling print that don't print a new, line which is done by adding a , at the end like print x, with that in mind then the changes needed are
n = int(raw_input())
for i in range(n):
pairs = raw_input().split() #by default split use spaces
pair_sum = 0
for p in pairs:
pair_sum += int(p) # a += b is the same as a = a + b
print pair_sum,
print "" # to print a new line so any future print is not done in the same line as the previous one
that was the version with print per line, next is the version using list
n = int(raw_input())
resul_per_line = []
for i in range(n):
pairs = raw_input().split() #by default split use spaces
pair_sum = 0
for p in pairs:
pair_sum += int(p) # a += b is the same as a = a + b
resul_per_line.append( str(pair_sum) ) #conver each number to a string to use with join bellow
print " ".join(resul_per_line)
with either of the above let said for example that the input data is
3
1 2
40 50
600 700
then the result would be
3 90 1300
some parts of the above code can be simplify by using built in functions like map and sum, for example this part
pair_sum = 0
for p in pairs:
pair_sum += int(p)
can become
pair_sum = sum( map(int,pairs) )
Uh oh, it looks like you're reusing the same variable i in the inner loop as the outer loop -- this is bad practice and can lead to bugs down the road.
What you're doing currently is adding both elements in each pair to sum and then printing that at the end, you can fix this in two different ways.
You can sum each pair, convert the sum to a string, and then concatenate that with your the rest of the sums as strings, or
You can print the sum of each pair immediately after summing them with print sum, which will print the number without the newline so that you can print all the results on a single line.
I want to decrypt an encrypted file. I'm having trouble all the way at the bottom when converting it and comparing it to a dictionary (which is full of words). Can someone guide me in the right direction? I'm struggling comparing the two.
#this function takes a string and encrypts ONLY letters by k shifts
def CaeserCipher(string, k):
#setting up variables to move through
upper = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'*10000
lower = 'abcdefghijklmnopqrstuvwxyz'*10000
newCipher = ''
#looping each letter and moving it k times
for letter in string:
if letter in upper:
if upper.index(letter) + k > 25:
indexPosition = (upper.index(letter) + k)
newCipher = newCipher + upper[indexPosition]
else:
indexPosition = upper.index(letter) + k
newCipher = newCipher + upper[indexPosition]
elif letter in lower:
if lower.index(letter) + k > 25:
indexPosition = (lower.index(letter) + k)
newCipher = newCipher + lower[indexPosition]
else:
indexPosition = lower.index(letter) + k
newCipher = newCipher + lower[indexPosition]
else:
newCipher = newCipher + letter
return newCipher
f = open('dictionary.txt', "r")
dictionary = set()
for line in f:
word = line.strip()
dictionary.add(word)
print dictionary
#main file
#reading file and encrypting text
f = open('encryptMystery1.txt')
string = ''
out = open("plain1.txt", "w")
myList = []
for line in f:
myList.append(line)
for sentence in myList:
for k in range(26):
updatedSentence = CaeserCipher(sentence, k)
for word in updatedSentence.split():
if word in dictionary:
out.write(updatedSentence)
break
print myList
f.close()
out.close()
Let's tackle this in steps, and the first step is entitled
WHY DO YOU HAVE 260,000 CHARACTER LONG STRINGS IN A CAESAR CIPHER
Sorry, I don't mean to be overly dramatic, but you realize that's going to take up more space than, well, Space, don't you? And it's completely unnecessary. It's an ugly and slow hack to avoid understanding the % (modulo) operator. Don't do that.
Now, to the modulo:
Step two of course will have to be understanding the modulo. It's not actually hard, it's just like the remainder of a division problem. You remember when you were in school and just LEARNING division? 7/4 was 1r3 not 1.75, remember? Well Python has functions for all that. 7/4 == 1.75, 7//4 == 1 and 7 % 4 == 3. This is useful because it can serve to "wrap" a number around a fixed length.
Let's say for example you have some string with 26 indexes (like, I don't know, an alphabet?). You're trying to add some number to a starting index, then return the result but UGH YOU'RE ADDING 2 TO Y AND IT DOESN'T WORK! Well with modulo it can. Y is in index 24 (remember zero is its own index), and 24+2 is 26 and there IS no 26th index. However, if you know there's going to be only 26 elements in your string, we can take the modulo and use THAT instead.
By that logic, index + CONSTANT % len(alphabet) will ALWAYS return the right number using simple math and not sweet baby jesus the quarter million element long string you just butchered.
Ugh your mother would be ashamed.
Reversing a Caesar cipher
So you've got a good idea, going through each line in turn and applying every kind of cipher to it. If I were you I'd dump them all into separate files, or even into separate list elements. Remember though that if you're reversing the cipher, you need to use -k not k. It's probably a good idea to simply change your Caesar cipher to detect that though, since the modulo trick doesn't work in this case. Try something like:
def cipher(text, k):
cipherkey = "SOMESTRINGGOESHERE"
if k < 0:
k = len(cipherkey) + k
# len(cipherkey) - abs(k) would be more clear, but if it HAS to be
# a negative number to get in here, it seems silly to add the call
# to abs
Then you can do:
startingtext = "Encrypted_text_goes_here"
possibledecrypts = [cipher(startingtext, -i) for i in range(1,26)]
Here is my question
count += 1
num = 0
num = num + 1
obs = obs_%d%(count)
mag = mag_%d%(count)
while num < 4:
obsforsim = obs + mag
mylist.append(obsforsim)
for index in mylist:
print index
The above code gives the following results
obs1 = mag1
obs2 = mag2
obs3 = mag3
and so on.
obsforrbd = parentV = {0},format(index)
cmds.dynExpression(nPartilce1,s = obsforrbd,c = 1)
However when i run the code above it only gives me
parentV = obs3 = mag3
not the whole list,it only gives me the last element of the list why is that..??
Thanks.
I'm having difficulty interpreting your question, so I'm just going to base this on the question title.
Let's say you have a list of items (they could be anything, numbers, strings, characters, etc)
myList = [1,2,3,4,"abcd"]
If you do something like:
for i in myList:
print(i)
you will get:
1
2
3
4
"abcd"
If you want to convert this to a string:
myString = ' '.join(myList)
should have:
print(myString)
>"1 2 3 4 abcd"
Now for some explanation:
' ' is a string in python, and strings have certain methods associated with them (functions that can be applied to strings). In this instance, we're calling the .join() method. This method takes a list as an argument, and extracts each element of the list, converts it to a string representation and 'joins' it based on ' ' as a separator. If you wanted a comma separated list representation, just replace ' ' with ','.
I think your indentations wrong ... it should be
while num < 4:
obsforsim = obs + mag
mylist.append(obsforsim)
for index in mylist:
but Im not sure if thats your problem or not
the reason it did not work before is
while num < 4:
obsforsim = obs + mag
#does all loops before here
mylist.append(obsforsim) #appends only last
The usual pythonic way to spit out a list of numbered items would be either the range function:
results = []
for item in range(1, 4):
results.append("obs%i = mag_%i" % (item, item))
> ['obs1 = mag_1', 'obs2 = mag_2', 'ob3= mag_3']
and so on (note in this example you have to pass in the item variable twice to get it to register twice.
If that's to be formatted into something like an expression you could use
'\n'.join(results)
as in the other example to create a single string with the obs = mag pairs on their own lines.
Finally, you can do all that in one line with a list comprehension.
'\n'.join([ "obs%i = mag_%i" % (item, item) for item in range (1, 4)])
As other people have pointed out, while loops are dangerous - its easier to use range