Project Euler #23 - Non Abundant Sums - python

This is the task:
Problem 23
A perfect number is a number for which the sum of its proper divisors is exactly equal to the number. For example, the sum of the proper divisors of 28 would be 1 + 2 + 4 + 7 + 14 = 28, which means that 28 is a perfect number.
A number n is called deficient if the sum of its proper divisors is less than n and it is called abundant if this sum exceeds n.
As 12 is the smallest abundant number, 1 + 2 + 3 + 4 + 6 = 16, the smallest number that can be written as the sum of two abundant numbers is 24. By mathematical analysis, it can be shown that all integers greater than 28123 can be written as the sum of two abundant numbers. However, this upper limit cannot be reduced any further by analysis >even though it is known that the greatest number that cannot be expressed as the sum of two abundant numbers is less than this limit.
Find the sum of all the positive integers which cannot be written as the sum of two abundant numbers.
This is my code:
import time
import math
start = time.time()
abundant_num_list = []
def checkAbundant():
for n in range(1, 28123):
factor_sum = 0
other_pair_factor = 0
for i in range(2, int(n ** 0.5) + 1):
if n % i == 0:
if math.floor(math.sqrt(n)) == math.sqrt(n):
other_pair_factor = 0
else:
other_pair_factor = n // i
factor_sum += (i + other_pair_factor + 1)
if n < factor_sum :
abundant_num_list.append(n)
def NonAbundantSums():
abundant_sum_list = []
all_num_list = []
non_abun_list = []
non_abun_sum = 0
for i in range(len(abundant_num_list)):
for j in range(i, len(abundant_num_list)):
if abundant_num_list[i] + abundant_num_list[j] <= 28123:
abundant_sum_list.append(abundant_num_list[i] + abundant_num_list[j])
for i in range(1, 28124):
all_num_list.append(i)
non_abun_list = [int(a) for a in (set(all_num_list) - set(abundant_sum_list))]
for i in range(len(non_abun_list)):
non_abun_sum += non_abun_list[i]
print(non_abun_sum)
checkAbundant()
NonAbundantSums()
end = time.time() - start
print("Done in", end, "seconds")
If it looks inefficient, i know, I'm new to coding. Python is my first programming language. I noticed a weird problem for my non_abun_list, where when retrieving the difference for set(all_num_list) and set(abundant_sum_list), the first and second index of abundant_sum_list is 2 and 30, so in my mind, non_abun_list shoud look like
[1, 2, 3, 4... ,22, 23, 25, 26, 27, 28, 29, 31, 32]
instead i got this
[1, 2, 3, 4... ,22, 23, 8209 ,25, 26, 27, 28, 29, 8219, 31, 32]
and i don't know how I got this list instead.
Can someone explain to me what's wrong with my code?
My result is 4352518 in ~25 seconds
Answer is 4179871

This is not an answer
(OP cannot participate in chat due to rep requirement)
You should consider your coding style. If you write concise functions to perform a task and have those functions return a value(s) then you can easily test those functions to see if they work. This makes it easier to determine what IS working.
For example when checking for abundancy you have to do two things: find the divisors of a number and compare their sum to that number.
def divisors(n):
'''return divisors of n'''
d = [1]
for i in range(2, int(pow(n,.5))+1):
if (n % i) == 0:
other = n // i
if other == i:
pair = [i]
else:
pair = [i,other]
d.extend(pair)
return d
def check(n):
'''return True if abundant'''
return sum(divisors(n)) > n
Now you can easily test both functions against known inputs and outputs if you start having problems. Once you know they work you don't have to consider them as a source of errors.
usage:
abundant_numbers = []
for n in range(12,28124):
if check(n):
abundant_numbers.append(n)
Test a couple of numbers:
>>> divisors(16)
[1, 2, 8, 4]
>>> divisors(1056)
[1, 2, 528, 3, 352, 4, 264, 6, 176, 8, 132, 11, 96, 12, 88, 16, 66, 22, 48, 24, 44, 32, 33]
>>> check(16)
False
>>> check(1056)
True
>>>
Yep, that looks right :).

for i in range(2, int(n ** 0.5) + 1):
if n % i == 0:
if math.floor(math.sqrt(n)) == math.sqrt(n) == i:
other_pair_factor = 0
else:
other_pair_factor = n // i
factor_sum += (i + other_pair_factor)
factor_sum += 1
For this specific part of checkAbundant(), i should include "== i " at line 3 because I only want the factor that will repeat twice to only count once for square numbers
For example, the pair factor as I would like to call for 36 is 1 x 36, 2 x 18, 3, 12, 4 x 9, 6 x 6.
For efficiency, I only find the first half of the factor pair, the other half is obtained through n // i. So in order to have the sum of proper factors of a number, I can't have repeated factors.
Without adding " ==i " I have made it so that for any square abundant numbers, their other half of the factor is not accounted for when summing the total factors up.
Another mistake i fixed in checkAbundant() is in line 8, where factor_sum += (i + other_pair_factor + 1)
This resulted in for every loop, factor_sum would have an additional 1, which will ruin the result. To fix that, i added 1 after the for loop
Overall, I would say it was a pretty rookie mistake. >.<

Related

Explain the logic of 'Count of N-digit numbers with absolute difference of adjacent digits not exceeding K' in Detail

Can someone please help me understand the logic of following Dynamic Programmming question Found this one at geeksforgeeks.com. I am unable to understand even after going through the answer provided.
Question:
Count of N-digit numbers with absolute difference of adjacent digits
not exceeding K | Set 2
Given two integers N and K, the task is to
find the count of N-digit numbers such that the absolute difference of
adjacent digits in the number is not greater than K.
Examples:
Input: N = 2, K = 1
Output: 26
Explanation: The numbers are 10, 11,
12, 21, 22, 23, 32, 33, 34, 43, 44, 45, 54, 55, 56, 65, 66, 67, 76,
77, 78, 87, 88, 89, 98, 99
Input: N = 3, K = 2
Output: 188
Python3 solution:
# Function to return the count of such numbers
def getCount(n, K):
# For 1-digit numbers, the count is 10 irrespective of K
if(n == 1):
return 10
# dp[j] stores the number of such i-digit numbers ending with j
dp = [0] * 11
# Stores the results of length i
next = [0] * 11
# Initialize count for 1-digit numbers
for i in range(1, 9 + 1):
dp[i] = 1
# Compute values for count of digits greater than 1
for i in range(2, n + 1):
for j in range(9 + 1):
# Find the range of allowed numbers if last digit is j
l = max(0, j - k)
r = min(9, j + k)
# Perform Range update
next[l] += dp[j]
next[r + 1] -= dp[j]
# Prefix sum to find actual count of i-digit numbers ending with j
for j in range(1, 9 + 1):
next[j] += next[j - 1]
# Update dp[]
for j in range(10):
dp[j] = next[j]
next[j] = 0
# Stores the final answer
count = 0
for i in range(9 + 1):
count += dp[i]
return count
if __name__ == '__main__':
n = 2
k = 1
print(getCount(n, k))
This code is contributed by Shivam Singh
Link: https://www.google.com/amp/s/www.geeksforgeeks.org/count-of-n-digit-numbers-with-absolute-difference-of-adjacent-digits-not-exceeding-k-set-2/amp/
I am unable to understand the logic in following lines
# Compute values for count of
# digits greater than 1
for i in range(2, n + 1):
for j in range(9 + 1):
# Find the range of allowed
# numbers if last digit is j
l = max(0, j - k)
r = min(9, j + k)
# Perform Range update
next[l] += dp[j]
next[r + 1] -= dp[j]
# Prefix sum to find actual count
# of i-digit numbers ending with j
for j in range(1, 9 + 1):
next[j] += next[j - 1]
# Update dp[]
for j in range(10):
dp[j] = next[j]
next[j] = 0
N=2 so we are dealing with strictly 2 digit numbers
K=1 so each digit in the current number shall be no more than 1 digit greater or less than its neighbors
While the answer is 26 (the count of such numbers), lets take a look at why the answer includes 10, 11, and 12 but not 13
10 is good as the absolute value of 1 - 0 (the digits of 10) is less than or equal to 1 (the value of K)
13 is bad as the absolute value of 1 - 3 is 2 and 2 is greater than K
Note that as N increases, the number of pairwise digit comparisons required will also increase.
I'll skip parsing someone else's code with meaningless variable names. Here is how I might attempt to solve this:
def is_valid_number(number_to_test, allowed_digit_spread):
if number_to_test < 10:
# apparently all 1 digit numbers pass
return True
# get the individual digits as a list
digits = [int(digit) for digit in str(number_to_test)]
for i in range(1, len(digits)):
current_digit = digits[i]
prior_digit = digits[i-1]
if abs(current_digit - prior_digit) > allowed_digit_spread:
# bad pairwise compare so reject
return False
return True
def get_numbers_to_test(allowed_digits):
start = pow(10, allowed_digits -1)
end = pow(10, allowed_digits)
return range(start, end)
def getCount(allowed_digits, allowed_digit_spread):
count = 0
for n in get_numbers_to_test(allowed_digits):
if is_valid_number(n, allowed_digit_spread):
count += 1
return count
if __name__ == '__main__':
n = 2
k = 1
print(getCount(n, k))

light gremlins - finding all the multiplication numbers of a prime in a range

I was trying to answer a question called "lightgremlins" on IEEEXTREME.
given a array number of gremlins and the prime numbers each chose return the numbers in range that are still on after all gremlins toggled there prime multiplied numbers.
given the input:
30 3 2 3 5
output:
15
case consists of a hallway of length 30, and three gremlins. The action of the gremlins is as
follows:
The first gremlin flips switches {2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30}. All of these
switches were previously off, so they are now on.
The second gremlin flips switches {3, 6, 9, 12, 15, 18, 21, 24, 27, 30}. Of these, {6, 12, 18, 24, 30}
were previously on, so they are now off. This results in the following switches being on: {2, 3, 4, 8, 9,
10, 14, 15, 16, 20, 21, 22, 26, 27, 28}.
The third gremlin flips switches {5, 10, 15, 20, 25, 30}. Of these, {10, 15, 20} were previously on, so
they are now off. This results in the following switches being on: {2, 3, 4, 5, 8, 9, 14, 16, 21, 22, 25,
26, 27, 28, 30}.
Thus, there are 15 switches on at the end of the night.
Now my code is very straight forward:
testcases=int(input())
for i in range(0,testcases):
array = input().split(' ')
arrayofnumbers = [int(x) for x in array]
#print(arrayofnumbers)
onCount=0
for j in range(1,arrayofnumbers[0]+1):
primeCount=0
for p in arrayofnumbers[2:len(arrayofnumbers)]:
if j%p == 0:
primeCount += 1
if primeCount % 2 == 1:
onCount += 1
print(onCount)
now this seems to work ok for small arrays or i think. i'm failing half of the test cases and i don't really understand why. perhaps this doesn't hold for very large arrays? perhaps my entire approach is wrong?
i have changed my code to use LCM and count amount of iterations but still this didn't solve the rest of the test cases pretty much stayed the same. this is my code:
from math import gcd
testcases=int(input())
for i in range(0,testcases):
array = input().split(' ')
arrayofnumbers = [int(x) for x in array]
#print(arrayofnumbers)
lcm = 1
for i in arrayofnumbers[2:]:
lcm = int(lcm * i / gcd(lcm, i))
#print(lcm)
if lcm >= arrayofnumbers[0]:
onCount=0
for j in range(1,arrayofnumbers[0]+1):
primeCount=0
for p in arrayofnumbers[2:len(arrayofnumbers)]:
if j%p == 0:
primeCount += 1
if primeCount % 2 == 1:
onCount += 1
print(onCount)
if lcm < arrayofnumbers[0]:
numiters=int(arrayofnumbers[0]/lcm)
onCount = 0
extraonCount=0
for j in range(1, lcm+1):
primeCount = 0
for p in arrayofnumbers[2:len(arrayofnumbers)]:
if j % p == 0:
primeCount += 1
if primeCount % 2 == 1:
onCount += 1
onCount = onCount * numiters
for j in range(1, (arrayofnumbers[0]-(lcm*numiters))+1):
primeCount = 0
for p in arrayofnumbers[2:len(arrayofnumbers)]:
if j % p == 0:
primeCount += 1
if primeCount % 2 == 1:
extraonCount += 1
onCount += extraonCount
print(onCount)
using a different approach using sets of divisible numbers i'm not getting any test case timeouts but wrong answers and a bit more correct test cases but still not a full right answer.
using this idea:
click link
testcases=int(input())
for i in range(0,testcases):
array = input().split(' ')
arrayofnumbers = [int(x) for x in array]
arrayOfon = []
arrayIterate = []
arrayPrimes = []
for j in arrayofnumbers[2:]:
arrayPrimes.append(j)
arrayPrimes.sort()
print(arrayPrimes)
for j in arrayPrimes:
num=0
num = int(arrayofnumbers[0] // j)
arrayOfon.append(num)
print(arrayOfon)
for j in arrayPrimes[1:]:
arrayIterate.append(j)
print(arrayIterate)
for j in range(0, len(arrayIterate)):
x = 0
y = 0
y = arrayOfon[0]
x = y // arrayIterate[j]
arrayOfon[0] = (y - x) + (arrayOfon[j+1] - x)
print(arrayOfon[0])
Let lcm be the least common multiple, the counts will repeat every lcm numbers, so you don't need to loop over the whole range, just count how many switches are on in the range 1..lcm, then multiply that by int(n/lcm) (i.e. how many times the sequence will repeat as whole) and add the counts for the last items, i.e. from lcm*int*n/lcm) to end.
Simple example:
if the number of switches is 33, and there are two gremlins: 2 and 3, lcm(2,3)=6 so you don't need to count all the way from 1 to 33, because the counts will repeat every 6 switches, and will repeat int(33/6)=5 times. So you only have to count the switches from 1 to 6, and multiply that count by 5, and add the count for the range 31...33

Automatically generate list from math function?

My idea is to run the 3n + 1 process (Collatz conjecture) on numbers ending in 1, 3, 7, and 9, within any arbitrary range, and to tell the code to send the lengths of each action to a list, so I can run functions on that list separately.
What I have so far is to specify unit digits 1,3,7 and 9 as: if n % 10 == 1; if n % 10 == 3 ...etc, and I think my plan needs some form of nested for loops; where I'm at with list appending is to have temp = [] and leng = [] and find a way for the code to automatically temp.clear() before each input to leng. I'm assuming there's different ways to do this, and I'm open to any ideas.
leng = []
temp = []
def col(n):
while n != 1:
print(n)
temp.append(n)
if n % 2 == 0:
n = n // 2
else:
n = n * 3 + 1
temp.append(n)
print(n)
It's unclear what specifically you're asking about and want to know, so this is only a guess. Since you only want to know the lengths of the sequences, there's no need to actually save the numbers in each one—which means there's only one list created.
def collatz(n):
""" Return length of Collatz sequence beginning with positive integer "n".
"""
count = 0
while n != 1:
n = n // 2 if n % 2 == 0 else n*3 + 1
count += 1
return count
def process_range(start, stop):
""" Return list of results of calling the collatz function to the all the
numbers in the closed interval [start...stop] that end with a digit
in the set {1, 3, 7, or 9}.
"""
return [collatz(n) for n in range(start, stop+1) if n % 10 in {1, 3, 7, 9}]
print(process_range(1, 42))
Output:
[0, 7, 16, 19, 14, 9, 12, 20, 7, 15, 111, 18, 106, 26, 21, 34, 109]

if a number is divisible by all the entries of a list then

This came up while attempting Problem 5 of Project Euler, I'm sorry if this is vague or obvious I am new to programming.
Suppose I have a list of integers
v = range(1,n) = [1, ..., n]
What I want to do is this:
if m is divisible by all the entries of v then I want to set
m/v[i] for i starting at 2 and iterating up
then I want to keep repeating this process until I eventually get something which is not divisible by all the entries of v.
Here is a specific example:
let v=[1,2,3,4] and m = 24
m is divisible by 1, 2, 3, and 4, so we divide m by 2 giving us
m=12 which is divisible by 1, 2, 3, and 4 , so we divide by 3
giving us m=4 which is not divisible by 1, 2, 3, and 4. So we stop here.
Is there a way to do this in python using a combination of loops?
I think this code will solve your problem:
i=1
while(True):
w=[x for x in v if (m%x)==0]
if(w==v):
m/=v[i]
i+=1
continue
elif(m!=v):
break
Try this out of size, have a feeling this is what you were asking for:
v = [1,2,3,4]
m = 24
cont = True
c = 1
d = m
while cont:
d = d/c
for i in v:
if d % i != 0:
cont = False
result = d
break
c+=1
print (d)
Got an output of 4.
I think this piece of code should do what you're asking for:
v = [1,2,3,4]
m = 24
index = 1
done = False
while not done:
if all([m % x == 0 for x in v]):
m = m // v[index]
if index + 1 == len(v):
print('Exhausted v')
done = True
else:
index += 1
else:
done = True
print('Not all elements in v evenly divide m')
That said, this is not the best way to go about solving Project Euler problem 5. A more straightforward and faster approach would be:
solved = False
num = 2520
while not solved:
num += 2520
if all([num % x == 0 for x in [11, 13, 14, 16, 17, 18, 19, 20]]):
solved = True
print(num)
In this approach, we known that the answer will be a multiple of 2520, so we increment the value we're checking by that amount. We also know that the only values that need to be checked are in [11, 13, 14, 16, 17, 18, 19, 20], because the number in the range [1,20] that aren't in that list are factors of at least one of the numbers in the list.

Find integer with most divisor from list of integers

I am trying to write a function to find integer with most divisor from a list, but i am getting the wrong output. This is how my function looks.
def find_my_integer_divisor(mylist):
def find_divisor(k):
count =0
for i in range (1,k+1):
if k%i==0:
count +=1
return count
A=mylist[0]
for x in mylist [0:]:
A=find_divisor(x)
return A
It returns the count of the last entry in mylist. I know I have to compare the value counts from each entry and returns the integer with the most count but don't know how to do it.
This should work:
def find_divisor(k):
count =0
for i in range (1,k+1):
if k%i==0:
count +=1
return count
def find_my_integer_divisor(mylist):
return max(mylist, key=find_divisor)
Instead of actually finding all the proper factors, we can much more efficiently calculate the number possible by doing a prime factorization.
For example,
288 == 2**5 * 3**2
and the number of proper factors is
(5 + 1) * (2 + 1) - 1
^ ^ ^
number number omit one case:
of twos of threes 5 2s and 2 3s == 288,
used in used in which is not a proper
factor, factor factor of itself
0..5
(six possible
values)
To do a prime factorization, we need to start by generating primes:
def primes(known_primes=[7, 11, 13, 17, 19, 23, 29]):
"""
Generate every prime number in ascending order
"""
# 2, 3, 5 wheel
yield from (2, 3, 5)
yield from known_primes
# The first time the generator runs, known_primes
# contains all primes such that 5 < p < 2 * 3 * 5
# After each wheel cycle the list of known primes
# will be added to.
# We need to figure out where to continue from,
# which is the next multiple of 30 higher than
# the last known_prime:
base = 30 * (known_primes[-1] // 30 + 1)
new_primes = []
while True:
# offs is chosen so 30*i + offs cannot be a multiple of 2, 3, or 5
for offs in (1, 7, 11, 13, 17, 19, 23, 29):
k = base + offs # next prime candidate
for p in known_primes:
if not k % p:
# found a factor - not prime
break
elif p*p > k:
# no smaller prime factors - found a new prime
new_primes.append(k)
break
if new_primes:
yield from new_primes
known_primes.extend(new_primes)
new_primes = []
base += 30
which can be tested like
from itertools import islice
print(list(islice(primes(), 500)))
giving
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, ...
Now that we have primes, we can count the occurrences of each prime factor like so:
def prime_factor_count(n):
"""
Factorize n and yield (factor, count) for each distinct factor
"""
if n < 2:
return
else:
for p in primes():
count = 0
while not n % p:
count += 1
n //= p
if count:
yield (p, count)
if n == 1:
# number is fully factored
break
elif p*p > n:
# no smaller prime factors - remainder is prime
yield (n, 1)
break
which we can test like
print(list(prime_factor_count(288))) # => [(2, 5), (3, 2)]
which you should recognize from above, 288 == 2**5 * 3**2. Now we can
def num_proper_factors(n):
total_factors = 1
for factor, count in prime_factor_count(n):
total_factors *= (count + 1)
return total_factors - 1
which tests like
print(num_proper_factors(288)) # => 17
and finally,
def num_with_most_divisors(lst):
return max(lst, key=num_proper_factors)
QED.
short answer: use max with a key function like your's find_divisor as show by #rofls.
Long answer: in each iteration you need to compare yours previous value with the current value in your list, if the current value have a bigger count of divisor change A otherwise don't, the problem in your code is that you don't do this check. You can do something like this
def max_divisor_count(my_list):
result = my_list[0]
for n in my_list[1:]: # start in 1 because 0 is already in result
if find_divisor(n) > find_divisor(result):
result = n
return result
and this is more or less the same that the max with key-function solution does.
Of course this can be improved a little more to avoid repeat calculations like this
def max_divisor_count(my_list):
result = my_list[0]
div_count = find_divisor(result)
for n in my_list[1:]: # start in position 1 because 0 is already in result
if result != n:
temp = find_divisor(n)
if temp > div_count:
result = n
div_count = temp
return result
This is a generator expression alternative. Note I use itertools.tee to create 2 instances of the generator. The first is to calculate the max, the second to feed enumerate.
The below example also demonstrates how you can use a list comprehension to return all integers with the maximum number of divisors.
from itertools import tee
lst = [1, 2, 3, 6, 8, 10, 14]
gen1, gen2 = tee(sum(k%i==0 for i in range(1, k+1)) for k in lst)
divmax = max(gen1)
[lst[i] for i, j in enumerate(gen2) if j == divmax]
# [6, 8, 10, 14]

Categories