Project Euler #45: How is my logic wrong? - python

From Project Euler, problem 45:
Triangle, pentagonal, and hexagonal numbers are generated by the following formulae:
Triangle T_(n)=n(n+1)/2 1, 3, 6, 10, 15, ...
Pentagonal P_(n)=n(3n−1)/2 1, 5, 12, 22, 35, ...
Hexagonal H_(n)=n(2n−1) 1, 6, 15, 28, 45, ...
It can be verified that T_(285) = P_(165) = H_(143) = 40755.
Find the next triangle number that is also pentagonal and hexagonal.
[ http://projecteuler.net/problem=45 ]
Now to solve them I took three variables and equated the equations to A.
n(n + 1)/2 = a(3a - 1)/2 = b(2b - 1) = A
A = number at which the threee function coincide for values of n, a, b
Resultant we get 3 equations with n and A. Solving with quarditic formula, we get 3 equations.
(-1 + sqrt(1 + 8*A ) )/2
( 1 + sqrt(1 + 24*A) )/6
( 1 + sqrt(1 + 8*A ) )/4
So my logic is to test for values of A at which the three equation give a natural +ve value. So far it works correct for number 40755 but fails to find the next one upto 10 million.
(Edit): Here is my code in python
from math import *
i=10000000
while(1):
i = i + 1
if(((-1+sqrt(1+8*i))/2).is_integer()):
if(((1+sqrt(1+24*i))/6).is_integer()):
if(((1+sqrt(1+8*i))/4).is_integer()):
print i
break
How is my logic wrong? (Apologies for a bit of maths involved. :) )

Given that:
all hexagonals are also triangulars
heapq.merge is quite handy for the task at hand (efficient and saves code)
then this:
import heapq
def hexagonals():
"Simplified generation of hexagonals"
n= 1
dn= 5
while 1:
yield n
n+= dn
dn+= 4
def pentagonals():
n= 1
dn= 4
while 1:
yield n
n+= dn
dn+= 3
def main():
last_n= 0
for n in heapq.merge(hexagonals(), pentagonals()):
if n == last_n:
print n
last_n= n
main()
produces 1, 40755 and the other number you're seeking in almost no time, and a few seconds later a 14-digit number. Just stop the program when you think you burned enough electricity.
In case you want to avoid “opaque” libraries, use the following main (essentially the same algorithm, only spelled out):
def main():
hexagonal= hexagonals()
pentagonal= pentagonals()
h= next(hexagonal)
p= next(pentagonal)
while 1:
while p < h:
p= next(pentagonal)
if p == h:
print p
h= next(hexagonal)
Times look similar, but I didn't bother to benchmark.

Your logic is not wrong, your program just takes a long time to run (by my estimate it should provide an answer in about an hour). I know the answer and tested your program by setting i to a value just below it. Your program then popped out the right answer at once.
Heed the advice of ypercube.

simplest way to implement is to make 3 generators for each sequence and route them in
heapq.merge
and then if you find 3 same consecitive keys you got solution
Simplest way to find this is usnig
itertools.groupby

Related

how to find 3 Numbers with Sum closest to a given number

I'm trying to write simple code for that problem. If I get an array and number I need to find the 3 numbers that their sum are close to the number that's given.
I've thought about first to pop out the last digit (the first number)
then I'll have a new array without this digit. So now I look for the second number who needs to be less the sum target. so I take only the small numbers that it's smaller them the second=sum-first number (but I don't know how to choose it.
The last number will be third=sum-first-second
I tried to write code but it's not working and it's very basic
def f(s,target):
s=sorted(s)
print(s)
print(s[0])
closest=s[0]+s[1]+s[2]
m=s[:-1]
print(m)
for i in range(len(s)):
for j in range(len(m)):
if (closest<=target-m[0]) and s[-1] + m[j] == target:
print (m[j])
n = m[:j] + nums[j+1:]
for z in range (len(z)):
if (closest<target-n[z]) and s[-1]+ m[j]+n[z] == target:
print (n[z])
s=[4,2,12,3,4,8,14]
target=20
f(s,target)
if you have idea what to change here. Please let me know
Thank you
Here is my solution I tried to maximize the performance of the code to not repeat any combinations. Let me know if you have any questions.
Good luck.
def find_3(s,target):
to_not_rep=[] #This list will store all combinations without repetation
close_to_0=abs(target - s[0]+s[1]+s[2]) #initile
There_is_one=False #False: don't have a combination equal to the target yet
for s1,first_n in enumerate(s):
for s2,second_n in enumerate(s):
if (s1==s2) : continue #to not take the same index
for s3,third_n in enumerate(s):
if (s1==s3) or (s2==s3) : continue #to not take the same index
val=sorted([first_n,second_n,third_n]) #sorting
if val in to_not_rep :continue #to not repeat the same combination with diffrent positions
to_not_rep.append(val)#adding all the combinations without repetation
sum_=sum(val) #the sum of the three numbers
# Good one
if sum_==target:
print(f"Found a possibility: {val[0]} + {val[1]} + {val[2]} = {target}")
There_is_one = True
if There_is_one is False: #No need if we found combination equal to the target
# close to the target
# We know that (target - sum) should equal to 0 otherwise :
# We are looking for the sum of closet combinations(in abs value) to 0
pos_n=abs(target-sum_)
if pos_n < close_to_0:
closet_one=f"The closet combination to the target is: {val[0]} + {val[1]} + {val[2]} = {sum_} almost {target} "
close_to_0=pos_n
# Print the closet combination to the target in case we did not find a combination equal to the target
if There_is_one is False: print(closet_one)
so we can test it :
s =[4,2,3,8,6,4,12,16,30,20,5]
target=20
find_3(s,target)
#Found a possibility: 4 + 4 + 12 = 20
#Found a possibility: 2 + 6 + 12 = 20
#Found a possibility: 3 + 5 + 12 = 20
another test :
s =[4,2,3,8,6,4,323,23,44]
find_3(s,target)
#The closet combination to the target is: 4 + 6 + 8 = 18 almost 20
This is a simple solution that returns all possibilites.
For your case it completed in 0.002019 secs
from itertools import combinations
import numpy as np
def f(s, target):
dic = {}
for tup in combinations(s, 3):
try:
dic[np.absolute(np.sum(tup) - target)].append(str(tup))
except KeyError:
dic[np.absolute(np.sum(tup) - target)] = [tup]
print(dic[min(dic.keys())])
Use itertools.combinations to get all combinations of your numbers without replacement of a certain length (three in your case). Then take the three-tuple for which the absolute value of the difference of the sum and target is minimal. min can take a key argument to specify the ordering of the iterable passed to the function.
from typing import Sequence, Tuple
def closest_to(seq: Sequence[float], target: float, length: int = 3) -> Tuple[float]:
from itertools import combinations
combs = combinations(seq, length)
diff = lambda x: abs(sum(x) - target)
return min(combs, key=diff)
closest_to([4,2,12,3,4,8,14], 20) # (4, 2, 14)
This is not the fastest or most efficient way to do it, but it's conceptionally simple and short.
Something like this?
import math
num_find = 1448
lst_Results = []
i_Number = num_find
while i_Number > 0:
num_Exp = math.floor(math.log(i_Number) / math.log(2))
lst_Results.append(dict({num_Exp: int(math.pow(2, num_Exp))}))
i_Number = i_Number - math.pow(2, num_Exp)
print(lst_Results)
In a sequence of numbers: for example 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, etc ...
The sum of the previous numbers is never greater than the next. This gives us the possibility of combinations, for example:
The number: 1448, there is no other combination than the sum of the previous numbers: 8 + 32 + 128 + 256 + 1024
Then you find the numbers whose sum is close to the number provided

Using the inverse sec function in Python

I am creating a program that calculates the optimum angles to fire a projectile from a range of heights and a set initial velocity. Within the final equation I need to utilise, there is an inverse sec function present that is causing some troubles.
I have imported math and attempted to use asec(whatever) however it seems math can not calculate inverse sec functions? I also understand that sec(x) = 1/cos(x) but when I sub 1/cos(x) into the equation instead and algebraically solve for x it becomes a non real result :/.
The code I have is as follows:
print("This program calculates the optimum angles to launch a projectile from a given range of heights and a initial speed.")
x = input("Input file name containing list of heights (m): ")
f = open(x, "r")
for line in f:
heights = line
print("the heights you have selected are : ", heights)
f.close()
speed = float(input("Input your initial speed (m/s): "))
print("The initial speed you have selected is : ", speed)
ran0 = speed*speed/9.8
print(ran0)
f = open(x, "r")
for line in f:
heights = (line)
import math
angle = (math.asec(1+(ran0/float(heights))))/2
print(angle)
f.close()
So my main question is, is there any way to find the inverse sec of anything in python without installing and importing something else?
I realise this may be more of a math based problem than a coding problem however any help is appreciated :).
Let's say we're looking for real number x whose arcsecant is angle θ. Then we have:
θ = arcsec(x)
sec(θ) = x
1 / cos(θ) = x
cos(θ) = 1 / x
θ = arccos(1/x)
So with this reasoning, you can write your arcsecant function as:
from math import acos
def asec(x):
return acos(1/x)
"I also understand that sec(x) = 1/cos(x) but when I sub 1/cos(x) ..." Do you have to use sec or asec ?
Because sec(x)= 1/cos(x) and asec(x) = acos(1/x). Be careful the notation ^-1 is ambiguous, cos^-1(x) = acos(x) is different of [cos(x)]^-1.
angle = (math.asec(1+(ran0/float(heights))))/2
asec is not defined from -1 to 1
If you have a height lower than zero, and so the result of (ran0/float(heights)) is between -2 and 0, your angle will be non real.
I don't really know if this is what you asked for, but I hope it helps.
If math is OK for you to import, then you can use:
import math
def asec(x):
if x == 0:
return 1j * math.inf
else:
return math.acos(1 / x)
For some other ways of of re-writing asec(x), feast your eyes on the relevant Wikipedia article.
Alternatively, you could use Taylor series expansions, which always come in polynomial form, so, although that is only an approximation in a neighborhood of a given point, it would not require math.
For asec(x), its Taylor expansion in a neighborhood of ±∞ (also known as Laurent series) is given by (without using math):
def asec_taylor(x, pi=3.14159265):
if x == 0:
return 1j * float('inf')
else:
return pi / 2 - 1 / x - 1 / (6 * x ** 3) - 3 / (40 * x ** 5)
You can quickly check that the farther you are from 0, the better the approximation holds:
for x in range(-10, 10):
print(x, asec(x), asec_taylor(x))
-10 1.6709637479564565 1.670963741666667
-9 1.6821373411358604 1.6821373299281108
-8 1.696124157962962 1.6961241346516926
-7 1.714143895700262 1.7141438389326868
-6 1.7382444060145859 1.7382442416666668
-5 1.7721542475852274 1.7721536583333335
-4 1.8234765819369754 1.823473733854167
-3 1.9106332362490186 1.910611139814815
-2 2.0943951023931957 2.0939734083333335
-1 3.141592653589793 2.8124629916666666
0 (nan+infj) (nan+infj)
1 0 0.32912965833333346
2 1.0471975511965979 1.0476192416666668
3 1.2309594173407747 1.2309815101851853
4 1.318116071652818 1.3181189161458333
5 1.369438406004566 1.3694389916666667
6 1.4033482475752073 1.4033484083333334
7 1.4274487578895312 1.4274488110673134
8 1.4454684956268313 1.4454685153483076
9 1.4594553124539327 1.4594553200718894
If you can try of inverse of sec then it will be same as
>>>from mpmath import *
>>> asec(-1)
mpf('3.1415926535897931')
Here are the link in where you can better understand - [http://omz-software.com/pythonista/sympy/modules/mpmath/functions/trigonometric.html]

find if a number divisible by the input numbers

Given two numbers a and b, we have to find the nth number which is divisible by a or b.
The format looks like below:
Input :
First line consists of an integer T, denoting the number of test cases.
Second line contains three integers a, b and N
Output :
For each test case, print the Nth
number in a new line.
Constraints :
1≤t≤105
1≤a,b≤104
1≤N≤10
Sample Input
1
2 3 10
Sample Output
15
Explanation
The numbers which are divisible by 2
or 3 are: 2,3,4,6,8,9,10,12,14,15 and the 10th number is 15
My code
test_case=input()
if int(test_case)<=100000 and int(test_case)>=1:
for p in range(int(test_case)):
count=1
j=1
inp=list(map(int,input().strip('').split()))
if inp[0]<=10000 and inp[0]>=1 and inp[1]<=10000 and inp[1]>=1 and inp[1]<=1000000000 and inp[1]>=1:
while(True ):
if count<=inp[2] :
k=j
if j%inp[0]==0 or j%inp[1] ==0:
count=count+1
j=j+1
else :
j=j+1
else:
break
print(k)
else:
break
Problem Statement:
For single test case input 2000 3000 100000 it is taking more than one second to complete.I want if i can get the results in less than 1 second. Is there a time efficient approach to this problem,may be if we can use some data structure and algorithms here??
For every two numbers there will be number k such that k=a*b. There will only be so many multiples of a and b under k. This set can be created like so:
s = set(a*1, b*1, ... a*(b-1), b*(a-1), a*b)
Say we take the values a=2, b=3 then s = (2,3,4,6). These are the possible values of c:
[1 - 4] => (2,3,4,6)
[5 - 8] => 6 + (2,3,4,6)
[9 - 12] => 6*2 + (2,3,4,6)
...
Notice that the values repeat with a predictable pattern. To get the row you can take the value of c and divide by length of the set s (call it n). The set index is the mod of c by n. Subtract 1 for 1 indexing used in the problem.
row = floor((c-1)/n)
column = `(c-1) % n`
result = (a*b)*row + s(column)
Python impl:
a = 2000
b = 3000
c = 100000
s = list(set([a*i for i in range(1, b+1)] + [b*i for i in range(1, a+1)]))
print((((c-1)//len(s)) * (a*b)) + s[(c - 1)%len(s)])
I'm not certain to grasp exactly what you're trying to accomplish. But if I get it right, isn't the answer simply b*(N/2)? since you are listing the multiples of both numbers the Nth will always be the second you list times N/2.
In your initial example that would be 3*10/2=15.
In the code example, it would be 3000*100000/2=150'000'000
Update:
Code to compute the desired values using set's and lists to speed up the calculation process. I'm still wondering what the recurrence for the odd indexes could be if anyone happens to stumble upon it...
a = 2000
b = 3000
c = 100000
a_list = [a*x for x in range(1, c)]
b_list = [b*x for x in range(1, c)]
nums = set(a_list)
nums.update(b_list)
nums = sorted(nums)
print(nums[c-1])
This code runs in 0.14s on my laptop. Which is significantly below the requested threshold. Nonetheless, this values will depend on the machine the code is run on.

Algorithm for distributing tasks to two printers?

I am doing the programming exercise online, and I found this question:
Two printers work in different speed. The first printer produces one paper in x minutes, while the second does it in y minutes. To print N papers in total, how to distribute the tasks to those printers so the printing time is minimum?
The exercise gives me three inputs x,y,N and asks for the minimum time as output.
input data:
1 1 5
3 5 4
answer:
3 9
I have tried to set tasks for first printer as a, and the tasks for the second printer as N-a. The most efficient way to print is to let them have the same time, so the minimum time would be ((n*b)/(a+b))+1. However this formula is wrong.
Then I tried to use a brute force way to solve this problem. I first distinguished which one is smaller (faster) in a and b. Then I keep adding one paper to the faster printer. When the time needed for that printer is longer than the time to print one paper of the other printer, I give one paper to the slower printer, and subtract the time of faster printer.
The code is like:
def fastest_time (a, b, n):
""" Return the smalles time when keep two machine working at the same time.
The parameter a and b each should be a float/integer referring to the two
productivities of two machines. n should be an int, refering to the total
number of tasks. Return an int standing for the minimal time needed."""
# Assign the one-paper-time in terms of the magnitude of it, the reason
# for doing that is my algorithm is counting along the faster printer.
if a > b:
slower_time_each = a
faster_time_each = b
elif a < b :
slower_time_each = b
faster_time_each = a
# If a and b are the same, then we just run the formula as one printer
else :
return (a * n) / 2 + 1
faster_paper = 0
faster_time = 0
slower_paper = 0
# Loop until the total papers satisfy the total task
while faster_paper + slower_paper < n:
# We keep adding one task to the faster printer
faster_time += 1 * faster_time_each
faster_paper += 1
# If the time is exceeding the time needed for the slower machine,
# we then assign one task to it
if faster_time >= slower_time_each:
slower_paper += 1
faster_time -= 1 * slower_time_each
# Return the total time needed
return faster_paper * faster_time_each
It works when N is small or x and y are big, but it needs a lot of time (more than 10 minutes I guess) to compute when x and y are very small, i.e. the input is 1 2 159958878.
I believe there is an better algorithm to solve this problem, can anyone gives me some suggestions or hints please?
Given the input in form
x, y, n = 1, 2, 159958878
this should work
import math
math.ceil((max((x,y)) / float(x+y)) * n) * min((x,y))
This works for all your sample inputs.
In [61]: x, y, n = 1,1,5
In [62]: math.ceil((max((x,y)) / float(x+y)) * n) * min((x,y))
Out[62]: 3.0
In [63]: x, y, n = 3,5,4
In [64]: math.ceil((max((x,y)) / float(x+y)) * n) * min((x,y))
Out[64]: 9.0
In [65]: x, y, n = 1,2,159958878
In [66]: math.ceil((max((x,y)) / float(x+y)) * n) * min((x,y))
Out[66]: 106639252.0
EDIT:
This does not work for the case mentioned by #Antti i.e. x, y, n = 4,7,2.
Reason is that we are considering smaller time first. So the solution is to find both the values i.e. considering smaller time and considering larger time, and then choose whichever of the resultant value is smaller.
So, this works for all the cases including #Antii's
min((math.ceil((max((x,y)) / float(x+y)) * n) * min((x,y)),
math.ceil((min((x,y)) / float(x+y)) * n) * max((x,y))))
Although there might be some extreme cases where you might have to change it a little bit.

Sum of Digits, properties, hint please

This is the problem:
How many integers 0 ≤ n < 10^18 have the property that the sum of the digits of n equals the sum of digits of 137n?
This solution is grossly inefficient. What am I missing?
#!/usr/bin/env python
#coding: utf-8
import time
from timestrings import *
start = time.clock()
maxpower = 18
count = 0
for i in range(0, 10 ** maxpower - 1):
if i % 9 == 0:
result1 = list(str(i))
result2 = list(str(137 * i))
sum1 = 0
for j in result1:
sum1 += int(j)
sum2 = 0
for j in result2:
sum2 += int(j)
if sum1 == sum2:
print (i, sum1)
count += 1
finish = time.clock()
print ("Project Euler, Project 290")
print ()
print ("Answer:", count)
print ("Time:", stringifytime(finish - start))
First of all, you are to count, not to show the hits.
That is very important. All you have to do is to device an efficient way to count it. Like Jon Bentley wrote in Programming Pearls: "Any methond that considers all permutations of letters for a word is doomed to failure". In fact, I tried in python, as soon as "i" hit 10^9, the system already freezed. 1.5 G memory was consumed. Let alone 10^18. And this also tells us, cite Bentley again, "Defining the problem was about ninety percent of this battle."
And to solve this problem, I can't see a way without dynamic programming (dp). In fact, most of those ridiculously huge Euler problems all require some sort of dp. The theory of dp itself is rather academic and dry, but to implement the idea of dp to solve real problems is not, in fact, the practice is fun and colorful.
One solution to the problem is, we go from 0-9 then 10-99 then 100-999 and so on and extract the signatures of the numbers, summarize numbers with the same signature and deal with all of them as a piece, thus save space and time.
Observation:
3 * 137 = 411 and 13 * 137 = 1781. Let's break the the first result "411" down into two parts: the first two digits "41" and the last digit "1". The "1" is staying, but the "41" part is going to be "carried" to further calculations. Let's call "41" the carry, the first element of the signature. The "1" will stay as the rightest digit as we go on calculating 13 * 137, 23 * 137, 33 * 137 or 43 * 137. All these *3 numbers have a "3" as their rightest digit and the last digit of 137*n is always 1. That is, the difference between this "3" and "1" is +2, call this +2 the "diff" as the second element of the signature.
OK, if we are gonna find a two-digit number with 3 as its last digit, we have to find a digit "m" that satisfies
diff_of_digitsum (m, 137*m+carry) = -2 (1)
to neutralize our +2 diff accumulated earlier. If m could do that, then you know m * 10 + 3, on the paper you write: "m3", is a hit.
For example, in our case we tried digit 1. diff_of_digitsum (digit, 137*digit+carry) = diff_of_digitsum (1, 137*1+41) = -15. Which is not -2, so 13 is not a hit.
Let's see 99. 9 * 137 = 1233. The "diff" is 9 - 3 = +6. "Carry" is 123. In the second iteration when we try to add a digit 9 to 9 and make it 99, we have diff_of_digitsum (digit, 137*digit+carry) = diff_of_digitsum (9, 137*9+123) = diff_of_digitsum (9, 1356) = -6 and it neutralizes our surplus 6. So 99 is a hit!
In code, we just need 18 iteration. In the first round, we deal with the single digit numbers, 2nd round the 2-digit numbers, then 3-digit ... until we get to 18-digit numbers. Make a table before the iterations that with a structure like this:
table[(diff, carry)] = amount_of_numbers_with_the_same_diff_and_carry
Then the iteration begins, you need to keep updating the table as you go. Add new entries if you encounter a new signature, and always update amount_of_numbers_with_the_same_diff_and_carry. First round, the single digits, populate the table:
0: 0 * 137 = 0, diff: 0; carry: 0. table[(0, 0)] = 1
1: 1 * 137 = 137. diff: 1 - 7 = -6; carry: 13. table[(-6, 13)] = 1
2: 2 * 137 = 274. diff: 2 - 7 = -5; carry: 27. table[(-5, 27)] = 1
And so on.
Second iteration, the "10"th digit, we will go over the digit 0-9 as your "m" and use it in (1) to see if it can produce a result that neutralizes the "diff". If yes, it means this m is going to make all those amount_of_numbers_with_the_same_diff_and_carry into hits. Hence counting not showing. And then we can calculate the new diff and carry with this digit added, like in the example 9 has diff 6 and carry 123 but 99 has the diff 9 - 6 ( last digit from 1356) = 3 and carry 135, replace the old table using the new info.
Last comment, be careful the digit 0. It will appear a lot of times in the iteration and don't over count it because 0009 = 009 = 09 = 9. If you use c++, make sure the sum is in unsigned long long and that sort because it is big. Good luck.
You are trying to solve a Project Euler problem by brute force. That may work for the first few problems, but for most problems you need think of a more sophisticated approach.
Since it is IMHO not OK to give advice specific to this problem, take a look at the general advice in this answer.
This brute force Python solution of 7 digits ran for 19 seconds for me:
print sum(sum(map(int, str(n))) == sum(map(int, str(137 * n)))
for n in xrange(0, 10 ** 7, 9))
On the same machine, single core, same Python interpreter, same code, would take about 3170 years to compute for 18 digits (as the problem asked).
See dgg32's answer for an inspiration of a faster counting.

Categories