Project Euler #13 in Python, trying to find smart solution - python

I'm trying to solve problem 13 from Euler project, and I'm trying to make the solution beautiful (at least, not ugly). Only "ugly thing" I do is that I'm pre-formating the input and keep it in the solution file (due to some technical reasons, and 'cause I want to concentrate on numeric part of problem)
The problem is "Work out the first ten digits of the sum of the following one-hundred 50-digit numbers."
I wrote some code, that should work, as far as I know, but it gives wrong result. I've checked input several times, it seems to be OK...
nums=[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]
result_sum = []
tmp_sum = 0
for j in xrange(50):
for i in xrange(100):
tmp_sum += nums[i] % 10
nums[i] =nums[i] / 10
result_sum.insert(0,int(tmp_sum % 10))
tmp_sum = tmp_sum / 10
for i in xrange(10):
print result_sum[i]

Your code works by adding all the numbers in nums like a person would: adding column by column. Your code does not work because when you are summing the far left column, you treat it like every other column. Whenever people get to the far left, they write down the entire sum. So this line
result_sum.insert(0,int(tmp_sum % 10))
doesn't work for the far left column; you need to insert something else into result_sum in that case. I would post the code, but 1) I'm sure you don't need it, and 2) it's agains the Project-Euler tag rules. If you would like, I can email it to you, but I'm sure that won't be necessary.

You could save the numbers in a file (with a number on each line), and read from it:
nums = []
with open('numbers.txt', 'r') as f:
for num in f:
nums.append(int(num))
# nums is now populated with all of the numbers, so do your actual algorithm
Also, it looks like you want to store the sum as an array of digits. The cool thing about Python is that it automatically handles large integers. Here is a quote from the docs:
Plain integers (also just called integers) are implemented using long in C, which gives them at least 32 bits of precision (sys.maxint is always set to the maximum plain integer value for the current platform, the minimum value is -sys.maxint - 1). Long integers have unlimited precision.
So using an array of digits isn't really necessary if you are working with Python. In C, it is another story...
Also, regarding your code, you need to factor in the digits in tmp_sum, which contains your carry-over digits. You can add them into result_sum like this:
while tmp_sum:
result_sum.insert(0,int(tmp_sum % 10))
tmp_sum /= 10
This will fix your issue. Here, it works.

Since you already have all the numbers in a list, you should be able to take the sum of them pretty easily. Then you just need to take the first ten digits of the sum. I won't put any code here, though.

As Simple as this :
Values.txt will contain all digits.
nums = []
with open("values.txt",'r') as f:
for num in f:
nums.append(int(num))
print(str(sum(nums))[:10])

Just as easy is storing it in csv and using pandas:
def foo():
import pandas as pd
table = pd.read_csv("data.txt", header = None, usecols = [0])
and then iterate through panda dataframe:
sum = 0
for x in range(len(table)):
sum += int(table[0][x])
return str(sum)[:10]
just keep in mind that Python handles the large digits for you.

Related

Python input multiple lines and spaces

Im trying to solve one of the a2oj problems "given three numbers a , b and c. print the total sum of the three numbers added to itself."
I came with this
import sys
numbers = [int(x) for x in sys.stdin.read().split()]
print(numbers[0] + numbers[1] + numbers[2])
I saw many topics but I cant figure out how to read just 3 values from input. I know I can stop this procces by typing CTRL+D, but is there any possibility to make it automatic (after reaching third value)?
Thanks
// Thanks for very quick answers, I made mistake and posted only Problem Statement without Input Format: "three numbers separated by bunch of spaces and/or new lines"
So for example input should look like this:
2
1 4
// Ok thanks to you guys finally I made this:
n = []
while len(n) < 3:
s=input()
i = s.split()
[n.append(int(j)) for j in i]
print(2 * sum(n))
It's working but when I sent my results I got Runtime Error. I have no idea why:
Link: https://a2oj.com/p?ID=346
You could just use:
sys.argv
import sys
numbers = [int(x) for x in sys.argv[1:4]]
print(numbers)
print(sum(numbers))
When inputs are given line by line.
from sys import stdin
sum = 0
for num in stdin.readline(4):
sum = sum + int(num)
print(sum)
When inputs are given on CLI.
from sys import argv
sum = 0
for num in argv[1:4]:
sum = sum + int(num)
print(sum)
Use Python strip() and split() functions as per your usecases
I am not sure what you are looking for, but it seems that you are looking for is the input function, from python's builtins:
x=input()
This reads any input from the user, as a string. You have then to convert it to a number if needed.
You can read three values:
x=input("First value:")
y=input("Second value:")
z=input("Third value:")
As you have now specified more precisely the problem statement, I edit my answer:
In your case, this is not very complicated. I am not going to give you the answer straight away, as it would defeat the point, but the idea is to wrap the input inside a while loop. Something like:
numbers=[]
while (you have less than 3 numbers):
(input one line and add the numbers to your list)
(print the sum of your numbers)
That way you are waiting for as many inputs as you need until you reach 3 numbers. By the way, depending on your input, you might have to check whether you do not get more than 3 numbers.
After seeing the update from the question author and linked the online judge question description, the tweak to his code needed is below. It's worth noting that the expected output is in float and has precision set to 6 and the output is 2 * sum of all inputs, not just sum. There is no description on this in the online judge question and you've to understand from the input vs output.
n = []
while len(n) < 3:
s = input()
i = s.split()
n.extend(float(j) for j in i)
print(format(2 * sum(n), '.6f'))
Screenshot below
But the first version of this answer is still valid to the first version of this question. Keeping them if anyone else is looking for the following scenarios.
To separate inputs by enter aka New lines:
numbers_List = []
for i in range(3):
number = int(input())
numbers_List.append(number)
print("Sum of all numbers: ", sum(numbers_List))
Screenshot:
To separate inputs by space aka Bunch of spaces:
Use map before taking input. I'd suggest using input as well instead of sys.stdin.read() to get input from users, separated by space, and ended by pressing Enter key.
Very easy implementation below for any number of inputs and to add using sum function on a list:
numbers = list(map(int, input("Numbers: ").split()))
print("Sum of all numbers: ", sum(numbers))
The screenshot below and link to the program is here
Read Python's Built-in Functions documentation to know more about all the functions I used above.

Resampling (upsampling, interpolating) a series of numbers

I have a comma separated series of integer values that I'd like to resample so that I have twice as many, where a new value is added half way between each of the existing values. For example, if this is my source:
1,5,11,9,13,21
the result would be:
1,3,5,8,11,10,9,11,13,17,21
In case that's not clear, I'm trying to add a number between each of the values in my source series, like this:
1 5 11 9 13 21
1 3 5 8 11 10 9 11 13 17 21
I've searched quite a bit and it seems that something like scipy.signal.resample or panda should work, but I'm completely new at this and I haven't been able to get it working. For example, here's one of my attempts with scipy:
import numpy as np
from scipy import signal
InputFileName = "sample.raw"
DATA250 = np.loadtxt(InputFileName, delimiter=',', dtype=int);
print(DATA250)
DATA500 = signal.resample(DATA250, 11)
print(DATA500)
Which outputs:
[ 1 5 11 9 13 21]
[ 1. -0.28829461 6.12324489 10.43251996 10.9108191 9.84503237
8.40293529 10.7641676 18.44182898 21.68506897 12.68267746]
Obviously I'm using signal.resample incorrectly. Is there a way I can do this with signal.resample or panda? Should I be using some other method?
Also, in my example all of source numbers have an integer half way in between. In my actual data, that won't be the case. So if two of the number are 10,15, the new number would be 12.5. However I'd like to have all of the resulting numbers be integers. So the new number that gets inserted would need to either be 12 or 13 (it doesn't matter to me which it is).
Note that once I get this working, the source file will actually be a comma separated list of 2,000 numbers and the output should be 4,000 numbers (or technically 3,999 since there won't be one added to the end). Also, this is going to be used to process something similar to an ECG recording- currently the ECG is sampled at 250 Hz for 8 seconds, which is then passed to a separate process to analyze the recording. However that separate process needs the recording to be sampled at 500 Hz. So the workflow will be that I'll take a 250 Hz recording every 8 seconds and upsample it to 500 Hz, then pass the resulting output to the analysis process.
Thanks for any guidance you can provide.
Since the interpolation is simple, you can do it by hand:
import numpy as np
a = np.array([1,5,11,9,13,21])
b = np.zeros(2*len(a)-1, dtype=np.uint32)
b[0::2] = a
b[1::2] = (a[:-1] + a[1:]) // 2
You can also use scipy.signal.resample this way:
import numpy as np
from scipy import signal
a = np.array([1,5,11,9,13,21])
b = signal.resample(a, len(a) * 2)
b_int = b.astype(int)
The trick is to have exactly twice the number of elements, so that odd points match your initial points. Also I think that the Fourier interpolation done by scipy.signal.resample is better for your ECG signal than the linear interpolation you're asking for.
Although I probably would just use NumPy here, pretty similar to J. Martinot-Lagarde's answer, you don't actually have to.
First, you can read a single row of comma-separated numbers with just the csv module:
with open(path) as f:
numbers = map(int, next(csv.reader(f))
… or just string operations:
with open(path) as f:
numbers = map(int, next(f).split(','))
And then you can interpolate that easily:
def interpolate(numbers):
last = None
for number in numbers:
if last is not None:
yield (last+number)//2
yield number
last=number
If you want it to be fully general and reusable, just take a function argument and yield function(last, number), and replace None with sentinel = object().
And now, all you need to do is join the results and write them:
with open(outpath, 'w') as f:
f.write(','.join(map(str, interpolate(numbers))))
Are there any advantages to this solution? Well, other than the read/split and join/write, it's purely lazy. And we can write lazy split and join functions pretty easily (or just do it manually). So if you ever had to deal with a billion comma-separated numbers instead of a thousand, that's all you'd have to change.
Here's a lazy split:
def isplit(s, sep):
start = 0
while True:
nextpos = s.find(sep, start)
if nextpos == -1:
yield s[start:]
return
yield s[start:nextpos]
start=nextpos+1
And you can use an mmap as a lazily-read string (well, bytes, but our data are pure ASCII, so that's fine):
with open(path, 'rb') as f:
with mmap.mmap(inf.fileno(), 0, access=mmap.ACCESS_READ) as mm:
numbers = map(int, isplit(mm, b','))
And let's use a different solution for lazy writing, just for variety:
def icsvwrite(f, seq, sep=','):
first = next(seq, None)
if not first: return
f.write(first)
for value in seq:
f.write(sep)
f.write(value)
So, putting it all together:
with open(inpath, 'rb') as inf, open(outpath, 'w') as outf:
with mmap.mmap(inf.fileno(), 0, access=mmap.ACCESS_READ) as mm:
numbers = map(int, isplit(mm, b','))
icsvwrite(outf, map(str, interpolate(numbers)))
But, even though I was able to slap this together pretty quickly, and all of the pieces are nicely reusable, I'd still probably use NumPy for your specific problem. You're not going to read a row of a billion numbers. You already have NumPy installed on the only machine that's ever going to run this script. The cost of importing it every 8 seconds (which you can solve by just having the script sleep between runs). So, it's hard to beat an elegant 3-line solution.
Since you suggested a pandas solution, here is one possibility:
import pandas as pd
import numpy as np
l = [1,4,11,9,14,21]
n = len(l)
df = pd.DataFrame(l, columns = ["l"]).reindex(np.linspace(0, n-1, 2*n-1)).interpolate().astype(int)
print(df)
It feels unnecessary complicated, though. I tag in pandas, so people more familiar with pandas functionality see it.

Sum of primes below 2,000,000 in python

I am attempting problem 10 of Project Euler, which is the summation of all primes below 2,000,000. I have tried implementing the Sieve of Erasthotenes using Python, and the code I wrote works perfectly for numbers below 10,000.
However, when I attempt to find the summation of primes for bigger numbers, the code takes too long to run (finding the sum of primes up to 100,000 took 315 seconds). The algorithm clearly needs optimization.
Yes, I have looked at other posts on this website, like Fastest way to list all primes below N, but the solutions there had very little explanation as to how the code worked (I am still a beginner programmer) so I was not able to actually learn from them.
Can someone please help me optimize my code, and clearly explain how it works along the way?
Here is my code:
primes_below_number = 2000000 # number to find summation of all primes below number
numbers = (range(1, primes_below_number + 1, 2)) # creates a list excluding even numbers
pos = 0 # index position
sum_of_primes = 0 # total sum
number = numbers[pos]
while number < primes_below_number and pos < len(numbers) - 1:
pos += 1
number = numbers[pos] # moves to next prime in list numbers
sum_of_primes += number # adds prime to total sum
num = number
while num < primes_below_number:
num += number
if num in numbers[:]:
numbers.remove(num) # removes multiples of prime found
print sum_of_primes + 2
As I said before, I am new to programming, therefore a thorough explanation of any complicated concepts would be deeply appreciated. Thank you.
As you've seen, there are various ways to implement the Sieve of Erasthotenes in Python that are more efficient than your code. I don't want to confuse you with fancy code, but I can show how to speed up your code a fair bit.
Firstly, searching a list isn't fast, and removing elements from a list is even slower. However, Python provides a set type which is quite efficient at performing both of those operations (although it does chew up a bit more RAM than a simple list). Happily, it's easy to modify your code to use a set instead of a list.
Another optimization is that we don't have to check for prime factors all the way up to primes_below_number, which I've renamed to hi in the code below. It's sufficient to just go to the square root of hi, since if a number is composite it must have a factor less than or equal to its square root.
We don't need to keep a running total of the sum of the primes. It's better to do that at the end using Python's built-in sum() function, which operates at C speed, so it's much faster than doing the additions one by one at Python speed.
# number to find summation of all primes below number
hi = 2000000
# create a set excluding even numbers
numbers = set(xrange(3, hi + 1, 2))
for number in xrange(3, int(hi ** 0.5) + 1):
if number not in numbers:
#number must have been removed because it has a prime factor
continue
num = number
while num < hi:
num += number
if num in numbers:
# Remove multiples of prime found
numbers.remove(num)
print 2 + sum(numbers)
You should find that this code runs in a a few seconds; it takes around 5 seconds on my 2GHz single-core machine.
You'll notice that I've moved the comments so that they're above the line they're commenting on. That's the preferred style in Python since we prefer short lines, and also inline comments tend to make the code look cluttered.
There's another small optimization that can be made to the inner while loop, but I let you figure that out for yourself. :)
First, removing numbers from the list will be very slow. Instead of this, make a list
primes = primes_below_number * True
primes[0] = False
primes[1] = False
Now in your loop, when you find a prime p, change primes[k*p] to False for all suitable k. (You wouldn't actually do multiply, you'd continually add p, of course.)
At the end,
primes = [n for n i range(primes_below_number) if primes[n]]
This should be a great deal faster.
Second, you can stop looking once your find a prime greater than the square root of primes_below_number, since a composite number must have a prime factor that doesn't exceed its square root.
Try using numpy, should make it faster. Replace range by xrange, it may help you.
Here's an optimization for your code:
import itertools
primes_below_number = 2000000
numbers = list(range(3, primes_below_number, 2))
pos = 0
while pos < len(numbers) - 1:
number = numbers[pos]
numbers = list(
itertools.chain(
itertools.islice(numbers, 0, pos + 1),
itertools.ifilter(
lambda n: n % number != 0,
itertools.islice(numbers, pos + 1, len(numbers))
)
)
)
pos += 1
sum_of_primes = sum(numbers) + 2
print sum_of_primes
The optimization here is because:
Removed the sum to outside the loop.
Instead of removing elements from a list we can just create another one, memory is not an issue here (I hope).
When creating the new list we create it by chaining two parts, the first part is everything before the current number (we already checked those), and the second part is everything after the current number but only if they are not divisible by the current number.
Using itertools can make things faster since we'd be using iterators instead of looping through the whole list more than once.
Another solution would be to not remove parts of the list but disable them like #saulspatz said.
And here's the fastest way I was able to find: http://www.wolframalpha.com/input/?i=sum+of+all+primes+below+2+million 😁
Update
Here is the boolean method:
import itertools
primes_below_number = 2000000
numbers = [v % 2 != 0 for v in xrange(primes_below_number)]
numbers[0] = False
numbers[1] = False
numbers[2] = True
number = 3
while number < primes_below_number:
n = number * 3 # We already excluded even numbers
while n < primes_below_number:
numbers[n] = False
n += number
number += 1
while number < primes_below_number and not numbers[number]:
number += 1
sum_of_numbers = sum(itertools.imap(lambda index_n: index_n[1] and index_n[0] or 0, enumerate(numbers)))
print(sum_of_numbers)
This executes in seconds (took 3 seconds on my 2.4GHz machine).
Instead of storing a list of numbers, you can instead store an array of boolean values. This use of a bitmap can be thought of as a way to implement a set, which works well for dense sets (there aren't big gaps between the values of members).
An answer on a recent python sieve question uses this implementation python-style. It turns out a lot of people have implemented a sieve, or something they thought was a sieve, and then come on SO to ask why it was slow. :P Look at the related-questions sidebar from some of them if you want more reading material.
Finding the element that holds the boolean that says whether a number is in the set or not is easy and extremely fast. array[i] is a boolean value that's true if i is in the set, false if not. The memory address can be computed directly from i with a single addition.
(I'm glossing over the fact that an array of boolean might be stored with a whole byte for each element, rather than the more efficient implementation of using every single bit for a different element. Any decent sieve will use a bitmap.)
Removing a number from the set is as simple as setting array[i] = false, regardless of the previous value. No searching, not comparison, no tracking of what happened, just one memory operation. (Well, two for a bitmap: load the old byte, clear the correct bit, store it. Memory is byte-addressable, but not bit-addressable.)
An easy optimization of the bitmap-based sieve is to not even store the even-numbered bytes, because there is only one even prime, and we can special-case it to double our memory density. Then the membership-status of i is held in array[i/2]. (Dividing by powers of two is easy for computers. Other values are much slower.)
An SO question:
Why is Sieve of Eratosthenes more efficient than the simple "dumb" algorithm? has many links to good stuff about the sieve. This one in particular has some good discussion about it, in words rather than just code. (Nevermind the fact that it's talking about a common Haskell implementation that looks like a sieve, but actually isn't. They call this the "unfaithful" sieve in their graphs, and so on.)
discussion on that question brought up the point that trial division may be fast than big sieves, for some uses, because clearing the bits for all multiples of every prime touches a lot of memory in a cache-unfriendly pattern. CPUs are much faster than memory these days.

Singpath Python Error. "Your code took too long to return."

I was playing around with the Singpath Python practice questions. And came across a simple question which asks the following:
Given an input of a list of numbers and a high number,
return the number of multiples
of each of those numbers that are less than the maximum number.
For this case the list will contain a maximum of 3 numbers
that are all relatively prime to each other.
I wrote this simple program, it ran perfectly fine:
"""
Given an input of a list of numbers and a high number,
return the number of multiples
of each of those numbers that are less than the maximum number.
For this case the list will contain a maximum of 3 numbers
that are all relatively prime to each other.
>>> countMultiples([3],30)
9
>>> countMultiples([3,5],100)
46
>>> countMultiples([3,5,7],30)
16
"""
def countMultiples(l, max):
j = []
for num in l:
i = 1
count = 0
while num * i < max:
if num * i not in j:
j.append(num * i)
i += 1
return len(j)
print countMultiples([3],30)
print countMultiples([3,5],100)
print countMultiples([3, 5, 7],30)
But when I try to run the same on SingPath, it gave me this error
Your code took too long to return.
Your solution may be stuck in an infinite loop. Please try again.
Has anyone experienced the same issues with Singpath?
I suspect the error you're getting means exactly what it says. For some input that the test program gives your function, it takes too long to return. I don't know anything about singpath myself, so I don't know exactly how long that might be. But I'd guess that they give you enough time to solve the problem if you use the best algorithm.
You can see for yourself that your code is slow if you pass in a very large max value. Try passing 10000 as max and you may end up waiting for a minute or two to get a result.
There are a couple of reasons your code is slow in these situations. The first is that you have a list of every multiple that you've found so far, and you are searching the list to see if the latest value has already been seen. Each search takes time proportional to the length of the list, so for the whole run of the function, it takes quadratic time (relative to the result value).
You could improve on this quite a lot by using a set instead of a list. You can test if an object is in a set in (amortized) constant time. But if j is a set, you don't actually need to test if a value is already in it before adding, since sets ignore duplicated values anyway. This means you can just add a value to the set without any care about whether it was there already.
def countMultiples(l, max):
j = set() # use a set object, rather than a list
for num in l:
i = 1
count = 0
while num * i < max:
j.add(num*i) # add items to the set unconditionally
i += 1
return len(j) # duplicate values are ignored, and won't be counted
This runs a fair amount faster than the original code, and max values of a million or more will return in a not too unreasonable time. But if you try values larger still (say, 100 million or a billion), you'll eventually still run into trouble. That's because your code uses a loop to find all the multiples, which takes linear time (relative to the result value). Fortunately, there is a better algorithm.
(If you want to figure out the better approach on your own, you might want to stop reading here.)
The better way is to use division to find how many times you can multiply each value to get a value less than max. The number of multiples of num that are strictly less than max is (max-1) // num (the -1 is because we don't want to count max itself). Integer division is much faster than doing a loop!
There is an added complexity though. If you divide to find the number of multiples, you don't actually have the multiples themselves to put in a set like we were doing above. This means that any integer that is a multiple of more than than one of our input numbers will be counted more than once.
Fortunately, there's a good way to fix this. We just need to count how many integers were over counted, and subtract that from our total. When we have two input values, we'll have double counted every integer that is a multiple of their least common multiple (which, since we're guaranteed that they're relatively prime, means their product).
If we have three values, We can do the same subtraction for each pair of numbers. But that won't be exactly right either. The integers that are multiples of all three of our input numbers will be counted three times, then subtracted back out three times as well (since they're multiples of the LCM of each pair of values). So we need to add a final value to make sure those multiples of all three values are included in the final sum exactly once.
import itertools
def countMultiples(numbers, max):
count = 0
for i, num in enumerate(numbers):
count += (max-1) // num # count multiples of num that are less than max
for a, b in itertools.combinations(numbers, 2):
count -= (max-1) // (a*b) # remove double counted numbers
if len(numbers) == 3:
a, b, c = numbers
count += (max-1) // (a*b*c) # add the vals that were removed too many times
return count
This should run in something like constant time for any value of max.
Now, that's probably as efficient as you need to be for the problem you're given (which will always have no more than three values). But if you wanted a solution that would work for more input values, you can write a general version. It uses the same algorithm as the previous version, and uses itertools.combinations a lot more to get different numbers of input values at a time. The number of products of the LCM of odd numbers of values get added to the count, while the number of products of the LCM of even numbers of values are subtracted.
import itertools
from functools import reduce
from operator import mul
def lcm(nums):
return reduce(mul, nums) # this is only correct if nums are all relatively prime
def countMultiples(numbers, max):
count = 0
for n in range(len(numbers)):
for nums in itertools.combinations(numbers, n+1):
count += (-1)**n * (max-1) // lcm(nums)
return count
Here's an example output of this version, which is was computed very quickly:
>>> countMultiples([2,3,5,7,11,13,17], 100000000000000)
81947464300342

Why does this Python 3.3 code not work? digc not defined

It prints diga and digb but doesnt work with c! Any help? It's supposed to be a Denary to Binary converter but only 1-64, once i've cracked the code will increase this! Thanks so much
denaryno=int(input("Write a number from 1-64 "))
if 64%denaryno > 0:
diga=0
remaindera=(64%denaryno)
if 32/denaryno<1:
digb=1
remainderb=(denaryno%32)
else:
digb =0
if 16/remainderb<1:
digc=1
remainderc=(denaryno%16)
else:
digc=0
if 8/remainderc<1:
digd=1
remainderd=(denaryno%8)
else:
digd=0
if 4/remainderd<1:
dige=1
remaindere=(denary%4)
else:
dige=0
if 2/remaindere<1:
digf=1
remainderf=(denary%2)
else:
digf=0
if 1/remainderf<1:
digg=1
remainderg=(denary%1)
else:
digg=0
print (str(diga)+str(digb))
You only set digc in one of the top if/else statement. If 32/denaryno<1 is True, you don't set digc at all.
Set digc at the top of the function (to 0 or whatever else you want it to be). This applies to all the digit variables, digd, dige, etc.
What you really should do, instead, is use a list of digits, and append either a 0 or a 1 to that list every time you divide the number by a factor.
You may want to take a look at the divmod() function; it returns both the quotient and the remainder. You could also do with some looping here to slash the number of if statements needed here:
number = int(input("Write a number from 1-64 "))
digits = []
factor = 64
while number:
quotient, number = divmod(number, factor)
digits.append(quotient)
factor //= 2
print(''.join(map(str, digits)))
Wow that was a lot of work, you don't have to do all that.
def bin_convert(x, count=8):
return "".join(map(lambda y:str((x>>y)&1), range(count-1, -1, -1)))
here are the functions comprising this one from easy->important
str() returns a string
range() is a way to get a list from 1 number to another. Written like this range(count-1, -1, -1) counts backwards.
"".join() is a way to take an iterable and put the pieces together.
map() is a way to take a function and apply it to an iterable.
lambda is a way to write a function in 1 line. I was being lazy and could have written another def func_name(y) and it would have worked just as well.
>> is a way to shift bits. (which I believe understanding this one is the key component to understanding your problem)

Categories