(Python) Given two numbers A and B. I need to find all nested "groups" of numbers:
range(2169800, 2171194)
leading numbers: 21698XX, 21699XX, 2170XX, 21710XX, 217110X, 217111X,
217112X, 217113X, 217114X, 217115X, 217116X, 217117X, 217118X, 2171190X,
2171191X, 2171192X, 2171193X, 2171194X
or like this:
range(1000, 1452)
leading numbers: 10XX, 11XX, 12XX, 13XX, 140X, 141X, 142X, 143X,
144X, 1450, 1451, 1452
Harder than it first looked - pretty sure this is solid and will handle most boundary conditions. :) (There are few!!)
def leading(a, b):
# generate digit pairs a=123, b=456 -> [(1, 4), (2, 5), (3, 6)]
zip_digits = zip(str(a), str(b))
zip_digits = map(lambda (x,y):(int(x), int(y)), zip_digits)
# this ignores problems where the last matching digits are 0 and 9
# leading (12000, 12999) is same as leading(12, 12)
while(zip_digits[-1] == (0,9)):
zip_digits.pop()
# start recursion
return compute_leading(zip_digits)
def compute_leading(zip_digits):
if(len(zip_digits) == 1): # 1 digit case is simple!! :)
(a,b) = zip_digits.pop()
return range(a, b+1)
#now we partition the problem
# given leading(123,456) we decompose this into 3 problems
# lows -> leading(123,129)
# middle -> leading(130,449) which we can recurse to leading(13,44)
# highs -> leading(450,456)
last_digits = zip_digits.pop()
low_prefix = reduce(lambda x, y : 10 * x + y, [tup[0] for tup in zip_digits]) * 10 # base for lows e.g. 120
high_prefix = reduce(lambda x, y : 10 * x + y, [tup[1] for tup in zip_digits]) * 10 # base for highs e.g. 450
lows = range(low_prefix + last_digits[0], low_prefix + 10)
highs = range(high_prefix + 0, high_prefix + last_digits[1] + 1)
#check for boundary cases where lows or highs have all ten digits
(a,b) = zip_digits.pop() # pop last digits of middle so they can be adjusted
if len(lows) == 10:
lows = []
else:
a = a + 1
if len(highs) == 10:
highs = []
else:
b = b - 1
zip_digits.append((a,b)) # push back last digits of middle after adjustments
return lows + compute_leading(zip_digits) + highs # and recurse - woohoo!!
print leading(199,411)
print leading(2169800, 2171194)
print leading(1000, 1452)
def foo(start, end):
index = 0
is_lower = False
while index < len(start):
if is_lower and start[index] == '0':
break
if not is_lower and start[index] < end[index]:
first_lower = index
is_lower = True
index += 1
return index-1, first_lower
start = '2169800'
end = '2171194'
result = []
while int(start) < int(end):
index, first_lower = foo(start, end)
range_end = index > first_lower and 10 or int(end[first_lower])
for x in range(int(start[index]), range_end):
result.append(start[:index] + str(x) + 'X'*(len(start)-index-1))
if range_end == 10:
start = str(int(start[:index])+1)+'0'+start[index+1:]
else:
start = start[:index] + str(range_end) + start[index+1:]
result.append(end)
print "Leading numbers:"
print result
I test the examples you've given, it is right. Hope this will help you
This should give you a good starting point :
def leading(start, end):
leading = []
hundreds = start // 100
while (end - hundreds * 100) > 100:
i = hundreds * 100
leading.append(range(i,i+100))
hundreds += 1
c = hundreds * 100
tens = 1
while (end - c - tens * 10) > 10:
i = c + tens * 10
leading.append(range(i, i + 10))
tens += 1
c += tens * 10
ones = 1
while (end - c - ones) > 0:
i = c + ones
leading.append(i)
ones += 1
leading.append(end)
return leading
Ok, the whole could be one loop-level deeper. But I thought it might be clearer this way. Hope, this helps you...
Update :
Now I see what you want. Furthermore, maria's code doesn't seem to be working for me. (Sorry...)
So please consider the following code :
def leading(start, end):
depth = 2
while 10 ** depth > end : depth -=1
leading = []
const = 0
coeff = start // 10 ** depth
while depth >= 0:
while (end - const - coeff * 10 ** depth) >= 10 ** depth:
leading.append(str(const / 10 ** depth + coeff) + "X" * depth)
coeff += 1
const += coeff * 10 ** depth
coeff = 0
depth -= 1
leading.append(end)
return leading
print leading(199,411)
print leading(2169800, 2171194)
print leading(1000, 1453)
print leading(1,12)
Now, let me try to explain the approach here.
The algorithm will try to find "end" starting from value "start" and check whether "end" is in the next 10^2 (which is 100 in this case). If it fails, it will make a leap of 10^2 until it succeeds. When it succeeds it will go one depth level lower. That is, it will make leaps one order of magnitude smaller. And loop that way until the depth is equal to zero (= leaps of 10^0 = 1). The algorithm stops when it reaches the "end" value.
You may also notice that I have the implemented the wrapping loop I mentioned so it is now possible to define the starting depth (or leap size) in a variable.
The first while loop makes sure the first leap does not overshoot the "end" value.
If you have any questions, just feel free to ask.
Related
I want to make a pyramid of a string for an exercise.
I just don't know how to do it.
For example:
string = "these***are***just***random***words*"
and the pyramid I want to make is:
t
hes
e***a
re***ju
st***rand
om***words*
How do I do this?
def draw_pyramid(string, size):
if size > 15:
size = 15
if size < 5:
size = 5
l = string * size
for i in range(size + 1):
stars = i
p = l[0:stars]
spaces = size - i
print(" " * spaces + p)
def main():
size = int(input("How many layers do you want it to be?: "))
string = "these***are***just***random***words*"
draw_pyramid(string, size)
if __name__ == '__main__':
main()
Result:
t
th
the
thes
these
these*
these**
these***
these***a
these***ar
You can use string.center() to get the strings nicely aligned in the center. To get the right characters from l I use a start and an end variable:
def draw_pyramid(string, size):
if size > 15:
size = 15
if size < 5:
size = 5
length = size * 2 - 1
l = string * size
start = 0
end = 0
for stars in range(1, size + 1):
end += stars * 2 - 1
p = l[start:end]
start = end
print(p.center(length))
Output :
How many layers do you want it to be?: 10
t
hes
e***a
re***ju
st***rand
om***words*
these***are**
*just***random*
**words*these***a
re***just***random*
The way you are indexing l is wrong, based on what you want to achieve. If you study your desired output, you see that on the first row you want the substring l[0:1], on the second row l[1:4], then l[4:9], l[9:16] etc. The starting index is the sum of the first i odd elements, i.e. 1+3+5+.. and the stopping index is the sum of the first i+1 odd elements. Thus, at each step, you want l[i**2:(i+1)**2], with i starting at 0.
So your function should look like this
def draw_pyramid(string, size):
if size > 15:
size = 15
if size < 5:
size = 5
l = string * size
for i in range(size + 1):
p = l[i**2:(i+1)**2]
spaces = size - i
print(" " * spaces + p)
and it produces the desired output
I am creating a passkey of 16 alphanumeric characters where I am generating starting 4 digits with A001, A002, A003 till A999. Once it goes till A999, the alphabet will auto increase to B and digits will again start with 001. And the same process will go till Z999. Once the A-Z series will over, then it will start with AA01 and so on. How to do this thing in python? As I am new in python so I tried it on my own and also tried some examples but I am unable to make the increment of characters.
Any ideas or thoughts would be greatly appreciated.
Many thanks
rec=0
new_list16 = []
def autoIncrement():
global rec
first = 'A'
i = chr(ord(first))
new_list16.append(i)
while True:
pStart = 1 #adjust start value, if req'd
pInterval = 1 #adjust interval value, if req'd
if (rec == 0):
rec += pStart
else:
rec = rec + pInterval
return str(rec).zfill(3)
#print(autoIncrement())
new_list16.append(autoIncrement())
print(*new_list16, sep = '')
Going from A999 to B001 instead of B000 really messes things up a bit, but you can still use this for the A-Z part, and a simple modulo operation for the numbers.
def excel_format(num):
# see https://stackoverflow.com/a/182924/1639625
res = ""
while num:
mod = (num - 1) % 26
res = chr(65 + mod) + res
num = (num - mod) // 26
return res
def full_format(num, d=3):
chars = num // (10**d-1) + 1 # this becomes A..ZZZ
digit = num % (10**d-1) + 1 # this becomes 001..999
return excel_format(chars) + "{:0{}d}".format(digit, d)
for i in range(10000):
print(i, full_format(i, d=2))
Number of digits in the numeric part is controlled with the optional d parameter. I'll use 2 for purpose of demonstration, but 3 works just as well.
0 A01
...
98 A99
99 B01
...
2573 Z99
2574 AA01
...
9998 CW99
9999 CX01
def auto_increment(number):
if number == 'ZZZZ':
return 'ZZZZ'
digits = "".join([i for i in number if i.isdigit()])
chars = "".join([i for i in number if not i.isdigit()])
if int(digits) == int('9' * len(digits)):
digits = "000"
new_char = [ord(i) for i in chars]
if new_char[-1] % ord('Z') == 0:
new_char = "".join([chr(i) for i in new_char]) + 'A'
else:
new_char[-1] = new_char[-1] + 1
new_char = "".join([chr(i) for i in new_char])
else:
new_char = chars
new_digit = int(digits) + 1
l = len(new_char)
ll = len(str(new_digit))
new_digit = (("0" * (3-ll)) + str(new_digit))[(l-1):]
return f"{new_char}{new_digit}"
This function return you the next number, given any number.
for example: A999 will return AB01.
you can now just use this function in a loop.
This probably needs to be tested and refactored more, but here's a start for you:
def leadingZeros(number, digits):
numberString = str(number)
for digit in range(1, digits):
if number < 10**digit:
numberString = '0' + numberString
return numberString
def autoIncrement(oldNumber):
order = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ!'
lastDigitOrder = order.find(oldNumber[3])
newNumber = ''
if order.find(oldNumber[1]) <= 9:
# 3 digit number
number = int(oldNumber[1:]) + 1
letter = oldNumber[0]
if 1000 == number:
letterOrder = order.find(oldNumber[0])
letter = order[letterOrder + 1]
newNumber = letter + leadingZeros(number % 1000, 3)
elif order.find(oldNumber[2]) <= 9:
# 2 digit number
number = int(oldNumber[2:]) + 1
letters = oldNumber[0:2]
if 100 == number:
letterOrder = order.find(oldNumber[1])
letter = order[letterOrder + 1]
letters = oldNumber[0] + letter
newNumber = letters + leadingZeros(number % 100, 2)
elif order.find(oldNumber[3]) <= 9:
# 1 digit number
number = int(oldNumber[3]) + 1
letters = oldNumber[0:3]
if 10 == number:
letterOrder = order.find(oldNumber[2])
letter = order[letterOrder + 1]
letters = oldNumber[0:2] + letter
newNumber = letters + leadingZeros(number % 10, 1)
else:
# just letters
print(oldNumber)
letterOrder = order.find(oldNumber[3])
letter = order[letterOrder + 1]
newNumber = oldNumber[0:3] + letter
# if one of the digits has gone past Z then we need to update the letters
if '!' == newNumber[3]:
# past Z in 4th digit
letterOrder = order.find(oldNumber[2])
newNumber = newNumber[0:2] + order[letterOrder + 1] + 'A'
if '!' == newNumber[2]:
# past Z in 3rd digit
letterOrder = order.find(oldNumber[1])
newNumber = newNumber[0:1] + order[letterOrder + 1] + 'A' + newNumber[3]
if '!' == newNumber[1]:
# past Z in 2nd digit
letterOrder = order.find(oldNumber[0])
newNumber = order[letterOrder + 1] + 'A' + newNumber[2:]
return newNumber
print(autoIncrement('A999'))
print(autoIncrement('AA99'))
print(autoIncrement('AAA9'))
print(autoIncrement('AAAA'))
print(autoIncrement('AZZ9'))
This is not quite what you are asking for, but if your requirement is for 4-character "sequential" strings, let me suggest a far more simpler approach. Why not simply used base 36 numbers? That is, have your numbers go from 0, 1, 2, ... A, B, C, ... Z, 10, 11, 12, ... 1Z, ... Then to convert one of the base 36 strings to an int it is simply:
n = int('12AV', 36)
And to convert an int to a base n string:
def baseN(num, base, numerals="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"):
return ((num == 0) and numerals[0]) or (baseN(num // base, base, numerals).lstrip(numerals[0]) + numerals[num % base])
Putting it all together:
n = int('12AV', 36)
s = baseN(n + 1, 36)
print(s)
Prints:
12AW
You can, of course, start with 'A001' if you need to. You will then go to A00Z after 35 iterations. You will end up generating the same numbers as in your original method, just in a different order.
Thank you for the solutions you had provided. But I tried something exactly which I want for my question. Please check it and give your comments on it.
def full_format(i):
# limit of first range is 26 letters (A-Z) times 999 numbers (001-999)
if i < 26 * 999:
c,n = divmod(i,999) # quotient c is index of letter 0-25, remainder n is 0-998
c = chr(ord('A') + c) # compute letter
n += 1
return f'{c}{n:03}'
# After first range, second range is 26 letters times 26 letters * 99 numbers (01-99)
elif i < 26*999 + 26*26*99:
i -= 26*999 # remove first range offset
cc,n = divmod(i,99) # remainder n is 0-98, use quotient cc to compute two letters
c1,c2 = divmod(cc,26) # c1 is index of first letter, c2 is index of second letter
c1 = chr(ord('A') + c1) # compute first letter
c2 = chr(ord('A') + c2) # compute second letter
n += 1
return f'{c1}{c2}{n:02}'
else:
raise OverflowError(f'limit is {26*999+26*26*99}')
for i in range(92880, 92898):
print(full_format(i))
I am trying to solve USACO's Milking Cows problem. The problem statement is here: https://train.usaco.org/usacoprob2?S=milk2&a=n3lMlotUxJ1
Given a series of intervals in the form of a 2d array, I have to find the longest interval and the longest interval in which no milking was occurring.
Ex. Given the array [[500,1200],[200,900],[100,1200]], the longest interval would be 1100 as there is continuous milking and the longest interval without milking would be 0 as there are no rest periods.
I have tried looking at whether utilizing a dictionary would decrease run times but I haven't had much success.
f = open('milk2.in', 'r')
w = open('milk2.out', 'w')
#getting the input
farmers = int(f.readline().strip())
schedule = []
for i in range(farmers):
schedule.append(f.readline().strip().split())
#schedule = data
minvalue = 0
maxvalue = 0
#getting the minimums and maximums of the data
for time in range(farmers):
schedule[time][0] = int(schedule[time][0])
schedule[time][1] = int(schedule[time][1])
if (minvalue == 0):
minvalue = schedule[time][0]
if (maxvalue == 0):
maxvalue = schedule[time][1]
minvalue = min(schedule[time][0], minvalue)
maxvalue = max(schedule[time][1], maxvalue)
filled_thistime = 0
filled_max = 0
empty_max = 0
empty_thistime = 0
#goes through all the possible items in between the minimum and the maximum
for point in range(minvalue, maxvalue):
isfilled = False
#goes through all the data for each point value in order to find the best values
for check in range(farmers):
if point >= schedule[check][0] and point < schedule[check][1]:
filled_thistime += 1
empty_thistime = 0
isfilled = True
break
if isfilled == False:
filled_thistime = 0
empty_thistime += 1
if (filled_max < filled_thistime) :
filled_max = filled_thistime
if (empty_max < empty_thistime) :
empty_max = empty_thistime
print(filled_max)
print(empty_max)
if (filled_max < filled_thistime):
filled_max = filled_thistime
w.write(str(filled_max) + " " + str(empty_max) + "\n")
f.close()
w.close()
The program works fine, but I need to decrease the time it takes to run.
A less pretty but more efficient approach would be to solve this like a free list, though it is a bit more tricky since the ranges can overlap. This method only requires looping through the input list a single time.
def insert(start, end):
for existing in times:
existing_start, existing_end = existing
# New time is a subset of existing time
if start >= existing_start and end <= existing_end:
return
# New time ends during existing time
elif end >= existing_start and end <= existing_end:
times.remove(existing)
return insert(start, existing_end)
# New time starts during existing time
elif start >= existing_start and start <= existing_end:
# existing[1] = max(existing_end, end)
times.remove(existing)
return insert(existing_start, end)
# New time is superset of existing time
elif start <= existing_start and end >= existing_end:
times.remove(existing)
return insert(start, end)
times.append([start, end])
data = [
[500,1200],
[200,900],
[100,1200]
]
times = [data[0]]
for start, end in data[1:]:
insert(start, end)
longest_milk = 0
longest_gap = 0
for i, time in enumerate(times):
duration = time[1] - time[0]
if duration > longest_milk:
longest_milk = duration
if i != len(times) - 1 and times[i+1][0] - times[i][1] > longest_gap:
longes_gap = times[i+1][0] - times[i][1]
print(longest_milk, longest_gap)
As stated in the comments, if the input is sorted, the complexity could be O(n), if that's not the case we need to sort it first and the complexity is O(nlog n):
lst = [ [300,1000],
[700,1200],
[1500,2100] ]
from itertools import groupby
longest_milking = 0
longest_idle = 0
l = sorted(lst, key=lambda k: k[0])
for v, g in groupby(zip(l[::1], l[1::1]), lambda k: k[1][0] <= k[0][1]):
l = [*g][0]
if v:
mn, mx = min(i[0] for i in l), max(i[1] for i in l)
if mx-mn > longest_milking:
longest_milking = mx-mn
else:
mx = max((i2[0] - i1[1] for i1, i2 in zip(l[::1], l[1::1])))
if mx > longest_idle:
longest_idle = mx
# corner case, N=1 (only one interval)
if len(lst) == 1:
longest_milking = lst[0][1] - lst[0][0]
print(longest_milking)
print(longest_idle)
Prints:
900
300
For input:
lst = [ [500,1200],
[200,900],
[100,1200] ]
Prints:
1100
0
OK, I'm stuck on something seemingly simple. I am trying to convert a number to base 26 (ie. 3 = C, 27 = AA, ect.). I am guessing my problem has to do with not having a 0 in the model? Not sure. But if you run the code, you will see that numbers 52, 104 and especially numbers around 676 are really weird. Can anyone give me a hint as to what I am not seeing? I will appreciate it. (just in case to avoid wasting your time, # is ascii char 64, A is ascii char 65)
def toBase26(x):
x = int(x)
if x == 0:
return '0'
if x < 0:
negative = True
x = abs(x)
else:
negative = False
def digit_value (val):
return str(chr(int(val)+64))
digits = 1
base26 = ""
while 26**digits < x:
digits += 1
while digits != 0:
remainder = x%(26**(digits-1))
base26 += digit_value((x-remainder)/(26**(digits-1)))
x = remainder
digits -= 1
if negative:
return '-'+base26
else:
return base26
import io
with io.open('numbers.txt','w') as f:
for i in range(1000):
f.write('{} is {}\n'.format(i,toBase26(i)))
So, I found a temporary workaround by making a couple of changes to my function (the 2 if statements in the while loop). My columns are limited to 500 anyways, and the following change to the function seems to do the trick up to x = 676, so I am satisfied. However if any of you find a general solution for any x (may be my code may help), would be pretty cool!
def toBase26(x):
x = int(x)
if x == 0:
return '0'
if x < 0:
negative = True
x = abs(x)
else:
negative = False
def digit_value (val):
return str(chr(int(val)+64))
digits = 1
base26 = ""
while 26**digits < x:
digits += 1
while digits != 0:
remainder = x%(26**(digits-1))
if remainder == 0:
remainder += 26**(digits-1)
if digits == 1:
remainder -= 1
base26 += digit_value((x-remainder)/(26**(digits-1)))
x = remainder
digits -= 1
if negative:
return '-'+base26
else:
return base26
The problem when converting to Excel’s “base 26” is that for Excel, a number ZZ is actually 26 * 26**1 + 26 * 26**0 = 702 while normal base 26 number systems would make a 1 * 26**2 + 1 * 26**1 + 0 * 26**0 = 702 (BBA) out of that. So we cannot use the usual ways here to convert these numbers.
Instead, we have to roll our own divmod_excel function:
def divmod_excel(n):
a, b = divmod(n, 26)
if b == 0:
return a - 1, b + 26
return a, b
With that, we can create a to_excel function:
import string
def to_excel(num):
chars = []
while num > 0:
num, d = divmod_excel(num)
chars.append(string.ascii_uppercase[d - 1])
return ''.join(reversed(chars))
For the other direction, this is a bit simpler
import string
from functools import reduce
def from_excel(chars):
return reduce(lambda r, x: r * 26 + x + 1, map(string.ascii_uppercase.index, chars), 0)
This set of functions does the right thing:
>>> to_excel(26)
'Z'
>>> to_excel(27)
'AA'
>>> to_excel(702)
'ZZ'
>>> to_excel(703)
'AAA'
>>> from_excel('Z')
26
>>> from_excel('AA')
27
>>> from_excel('ZZ')
702
>>> from_excel('AAA')
703
And we can actually confirm that they work correctly opposite of each other by simply checking whether we can chain them to reproduce the original number:
for i in range(100000):
if from_excel(to_excel(i)) != i:
print(i)
# (prints nothing)
Simplest way, if you do not want to do it yourself:
from openpyxl.utils import get_column_letter
proper_excel_column_letter = get_column_letter(5)
# will equal "E"
Sorry, I wrote this in Pascal and know no Python
function NumeralBase26Excel(numero: Integer): string;
var
algarismo: Integer;
begin
Result := '';
numero := numero - 1;
if numero >= 0 then
begin
algarismo := numero mod 26;
if numero < 26 then
Result := Chr(Ord('A') + algarismo)
else
Result := NumeralBase26Excel(numero div 26) + Chr(Ord('A') + algarismo);
end;
end;
You can do it in one line (with line continuations for easier reading). Written here in VBA:
Function sColumn(nColumn As Integer) As String
' Return Excel column letter for a given column number.
' 703 = 26^2 + 26^1 + 26^0
' 64 = Asc("A") - 1
sColumn = _
IIf(nColumn < 703, "", Chr(Int((Int((nColumn - 1) / 26) - 1) / 26) + 64)) & _
IIf(nColumn < 27, "", Chr( ((Int((nColumn - 1) / 26) - 1) Mod 26) + 1 + 64)) & _
Chr( ( (nColumn - 1) Mod 26) + 1 + 64)
End Function
Or you can do it in the the worksheet:
=if(<col num> < 703, "", char(floor((floor((<col num> - 1) / 26, 1) - 1) / 26, 1) + 64)) &
if(<col num> < 27, "", char(mod( floor((<col num> - 1) / 26, 1) - 1, 26) + 1 + 64)) &
char(mod( <col num> - 1 , 26) + 1 + 64)
I've also posted the inverse operation done similarly.
Based on #TheUltimateOptimist's answer, I looked in the openpyxl implementation and found the "actual" algorithm used by openpyxl==3.0.10:
Be warned; it only supports values between 1 & 18278 (inclusive).
def _get_column_letter(col_idx):
"""Convert a column number into a column letter (3 -> 'C')
Right shift the column col_idx by 26 to find column letters in reverse
order. These numbers are 1-based, and can be converted to ASCII
ordinals by adding 64.
"""
# these indicies corrospond to A -> ZZZ and include all allowed
# columns
if not 1 <= col_idx <= 18278:
raise ValueError("Invalid column index {0}".format(col_idx))
letters = []
while col_idx > 0:
col_idx, remainder = divmod(col_idx, 26)
# check for exact division and borrow if needed
if remainder == 0:
remainder = 26
col_idx -= 1
letters.append(chr(remainder+64))
return ''.join(reversed(letters))
Find the greatest product of five consecutive digits in the 1000-digit number:
import time
num = '\
73167176531330624919225119674426574742355349194934\
96983520312774506326239578318016984801869478851843\
85861560789112949495459501737958331952853208805511\
12540698747158523863050715693290963295227443043557\
66896648950445244523161731856403098711121722383113\
62229893423380308135336276614282806444486645238749\
30358907296290491560440772390713810515859307960866\
70172427121883998797908792274921901699720888093776\
65727333001053367881220235421809751254540594752243\
52584907711670556013604839586446706324415722155397\
53697817977846174064955149290862569321978468622482\
83972241375657056057490261407972968652414535100474\
82166370484403199890008895243450658541227588666881\
16427171479924442928230863465674813919123162824586\
17866458359124566529476545682848912883142607690042\
24219022671055626321111109370544217506941658960408\
07198403850962455444362981230987879927244284909188\
84580156166097919133875499200524063689912560717606\
05886116467109405077541002256983155200055935729725\
71636269561882670428252483600823257530420752963450'
biggest = 0
i = 1
while i < len(num):
one = int(num[i])
two = int(num[i+1])
thr = int(num[i+2])
fou = int(num[i+3])
fiv = int(num[i+4])
product = one*two*thr*fou*fiv
if product > biggest:
biggest = product
i += i+1
print(product)
start = time.time()
elapsed = (time.time() - start)
print("This code took: " + str(elapsed) + " seconds")
This code gives me an answer of 7054, much too low, and should calculate many products along the way, but only calculates 9 of them. My question is: What is causing my code to deviate from its intended purpose and also, how can I optimize the part of the code calculating "one", "two", etc. in order to calculate the product?
There were several issues.
You were printing product not biggest. Make sure to print the right variable!
You were iterating through the length of the entire string when you should really just iterate in the range [0..len(num) - 4) so that you don't get an IndexError when you do your product calculation.
You were incrementing your i variable wrong. You want to increment it by 1 each turn so just do i += 1 or i = i + 1. The code i += i + 1 is equivalent to i = i + i + 1. I don't think you wanted to double i on every iteration of your while loop. :)
Sequences are 0-indexed in Python. This means when you are iterating through a set of indices, the first element is always at seq[0] and the elements continue until seq[n-1]. Therefore you should start your i variable at 0, not 1!
You were not measuring your time correctly. You want to have your start time be assigned to before all your code executes so that you can correctly measure elapsed time.
Here is your fixed code:
'''
Find the greatest product of five consecutive digits in the 1000-digit number
'''
import time
start = time.time()
num = '\
73167176531330624919225119674426574742355349194934\
96983520312774506326239578318016984801869478851843\
85861560789112949495459501737958331952853208805511\
12540698747158523863050715693290963295227443043557\
66896648950445244523161731856403098711121722383113\
62229893423380308135336276614282806444486645238749\
30358907296290491560440772390713810515859307960866\
70172427121883998797908792274921901699720888093776\
65727333001053367881220235421809751254540594752243\
52584907711670556013604839586446706324415722155397\
53697817977846174064955149290862569321978468622482\
83972241375657056057490261407972968652414535100474\
82166370484403199890008895243450658541227588666881\
16427171479924442928230863465674813919123162824586\
17866458359124566529476545682848912883142607690042\
24219022671055626321111109370544217506941658960408\
07198403850962455444362981230987879927244284909188\
84580156166097919133875499200524063689912560717606\
05886116467109405077541002256983155200055935729725\
71636269561882670428252483600823257530420752963450'
biggest = 0
i = 0
while i < len(num) - 4:
one = int(num[i])
two = int(num[i+1])
thr = int(num[i+2])
fou = int(num[i+3])
fiv = int(num[i+4])
product = one*two*thr*fou*fiv
if product > biggest:
biggest = product
i = i + 1
print(biggest)
elapsed = (time.time() - start)
print("This code took: " + str(elapsed) + " seconds")
Your problem is here:
i += i+1
You are walking through the list too fast. You should do this:
i += 1
I would write the code like this:
import operator
# Return the product of all the digits in `series` converted to integers
def numprod(series):
# Convert the series of digits into a list of integers
digits = [int(c) for c in series]
# This applies the multiplication operator to all the digits,
# starting with 1
return reduce(operator.__mul__, digits, 1)
# Produce every string of length 5
# This uses a generator but could just as easily use a list comprehension:
# numiter = [num[i : i + SERIES_SIZE] for i in range(len(num) - SERIES_SIZE + 1)]
SERIES_SIZE = 5
numiter = (num[i : i + SERIES_SIZE] for i in range(len(num) - SERIES_SIZE + 1))
# Calculate all the products
allnumprods = [numprod(series) for series in numiter]
# Find the maximum of all the products
print max(allnumprods)
A simpler way to calculate the product is this:
def numprod(series):
product = 1
for c in series:
product *= int(c)
return product
One can also use functional programing to simplify the problem considerably:
def foo():
max_product = 0
num = "string of input number"
for e,n in enumerate(num):
product = reduce((lambda x,y: x*y),map(int,(num[e:e+13])))
if product > max_product:
max_product = product
return max_product
give it a try!
The issue is here:
i += i+1
You are doubling i every time and adding 1 to it. So if i is 1, you make it 3. If it's 3, you make it 7. If it's 7, you make it 15, and so on. But this means your index is missing a lot of places, isn't it!
It is causing you to skip many positions in the number. You want to use:
i += 1
This just means add 1 to i.
Or you could do:
i = i+1
This means, set i equal to i+1.
import string
numbers=list()
fo=open("C:/python34/libs/maths2.txt","r+")
for eachline in fo:
for char in eachline:
if char.isdigit():
A=char
numbers.append(int(A))
print(numbers)
list1=list()
for i in range(0,len(numbers)-4):
x=numbers[i]*numbers[i+1]*numbers[i+2]*numbers[i+3]*numbers[i+4]
list1.append([x])
print(list1)
print(max(list1))
again this is not optimized but it works for 13 digits
import time
start_time = time.time()
num = "73167176531330624919225119674426574742355349194934\
96983520312774506326239578318016984801869478851843\
85861560789112949495459501737958331952853208805511\
12540698747158523863050715693290963295227443043557\
66896648950445244523161731856403098711121722383113\
62229893423380308135336276614282806444486645238749\
30358907296290491560440772390713810515859307960866\
70172427121883998797908792274921901699720888093776\
65727333001053367881220235421809751254540594752243\
52584907711670556013604839586446706324415722155397\
53697817977846174064955149290862569321978468622482\
83972241375657056057490261407972968652414535100474\
82166370484403199890008895243450658541227588666881\
16427171479924442928230863465674813919123162824586\
17866458359124566529476545682848912883142607690042\
24219022671055626321111109370544217506941658960408\
07198403850962455444362981230987879927244284909188\
84580156166097919133875499200524063689912560717606\
05886116467109405077541002256983155200055935729725\
71636269561882670428252483600823257530420752963450"
i = 0
j = 0
k = 0
while i < len(num) - 13:
one = int(num[i])
two = int(num[i + 1])
three = int(num[i + 2])
four = int(num[i + 3])
five = int(num[i + 4])
six = int(num[i + 5])
seven = int(num[i + 6])
eight = int(num[i + 7])
nine = int(num[i + 8])
ten = int(num[i + 9])
eleven = int(num[i + 10])
twoelve = int(num[i + 11])
thirteen = int(num[i + 12])
j = one * two * three * four * five * six * seven * eight * nine * ten * eleven * twoelve * thirteen
i = i + 1
if k < j:
k = j
print(k)
print(time.time() - start_time," seconds")
# 8 Largest product in a series
from functools import reduce
the_num = "7316717653133062491922511967442657474235534919493496983520312774506326239578318016984801869478851843858615607891129494954595017379583319528532088055111254069874715852386305071569329096329522744304355766896648950445244523161731856403098711121722383113622298934233803081353362766142828064444866452387493035890729629049156044077239071381051585930796086670172427121883998797908792274921901699720888093776657273330010533678812202354218097512545405947522435258490771167055601360483958644670632441572215539753697817977846174064955149290862569321978468622482839722413756570560574902614079729686524145351004748216637048440319989000889524345065854122758866688116427171479924442928230863465674813919123162824586178664583591245665294765456828489128831426076900422421902267105562632111110937054421750694165896040807198403850962455444362981230987879927244284909188845801561660979191338754992005240636899125607176060588611646710940507754100225698315520005593572972571636269561882670428252483600823257530420752963450"
the_num_lst = []
for n in the_num:
the_num_lst.append(int(n))
x = 0
y = reduce((lambda a, b: a * b), the_num_lst[x:x + 13])
while x < 1000 - 13:
x += 1
z = reduce((lambda a, b: a * b), the_num_lst[x:x + 13])
if z > y:
y = z
print(y)
My solution
s = '''7316717653133062491922511967442657474235534919493496983520312774506326239578318016984801869478851843858615607891129494954595017379583319528532088055111254069874715852386305071569329096329522744304355766896648950445244523161731856403098711121722383113622298934233803081353362766142828064444866452387493035890729629049156044077239071381051585930796086670172427121883998797908792274921901699720888093776657273330010533678812202354218097512545405947522435258490771167055601360483958644670632441572215539753697817977846174064955149290862569321978468622482839722413756570560574902614079729686524145351004748216637048440319989000889524345065854122758866688116427171479924442928230863465674813919123162824586178664583591245665294765456828489128831426076900422421902267105562632111110937054421750694165896040807198403850962455444362981230987879927244284909188845801561660979191338754992005240636899125607176060588611646710940507754100225698315520005593572972571636269561882670428252483600823257530420752963450'''
t = list(s)
#len(t) = 1000
#t[::-1] = t[999]
r = []
q = 0
while q <= 999:
w = int(t[q])
r.append(w)
q += 1
a = 0
b = 1
c = 2
d = 3
e = 4
f = 5
g = 6
h = 7
i = 8
j = 9
k = 10
l = 11
m = 12
y = []
while m<=999:
n = r[a]*r[b]*r[c]*r[d]*r[e]*r[f]*r[g]*r[h]*r[i]*r[j]*r[k]*r[l]*r[m]
y.append(n)
a += 1
b += 1
c += 1
d+= 1
e+= 1
f+= 1
g+= 1
h+= 1
i+= 1
j+= 1
l+= 1
k+= 1
m+= 1
if m >= 1000:
break
print(y)
print(max(y))
One-liner:
from functools import reduce
def max_prod(x):
return max([reduce(lambda x, y: int(x) * int(y), (x[i:i+5])) for i in range(len(x)-5)])
The solutions on here iterate over the series and iterate over a window (sub-series) in each iteration, which make them very slow for large window sizes.
The solution below computes the product in each iteration by using the product from the previous iteration, so there's no need to re-compute most of it (saves a loop over each window). To do that, it uses deque from the built-in collections module to keep a running window that allows to drop elements from each window efficiently.
The basic idea is, in each iteration, divide the product from the previous iteration by the first number used in its computation and multiply it by the number in the current iteration - similar to moving the window one slot to the right.
The main difficulty is that any number multiplied by 0 is 0, so once 0 enters a window, the product of the remaining numbers are lost. To recover that, two separate products are kept in each iteration: prod (which is the true running product) and after_zero (which is the product of non-zero numbers after a 0), and after_zero is assigned back to prod once the window doesn't contain a zero anymore.
from collections import deque
def compute_window(new_digit, prod, no_zero_prod, window, old_digit=1):
# compute product for this window
prod = prod // old_digit * new_digit
# if the new digit is zero, restart everything (because 0*number=0)
if new_digit == 0:
window, prod, no_zero_prod = deque(), 0, 1
else:
window.append(new_digit)
no_zero_prod = no_zero_prod // old_digit * new_digit
return prod, no_zero_prod, window
def greatest_product(num, window_size):
# initialize variable
running_window = deque()
prod = after_zero = 1
# calculate the initial product
for d in num[: window_size]:
prod, after_zero, running_window = compute_window(int(d), prod, after_zero, running_window)
# max value to beat
max_so_far = prod
for d in num[window_size :]:
# in each iteration, if the window is full, pop the element that was first entered
current_window_size = len(running_window)
prev = running_window.popleft() if current_window_size == window_size else 1
# if a single element is missing from window, assign the after_zero value to prod
# (which allows us to use it for this round's product computation)
if current_window_size == window_size - 1:
prod = after_zero
# computations for this iteration
prod, after_zero, running_window = compute_window(int(d), prod, after_zero, running_window, prev)
# update max_so_far
if prod > max_so_far:
max_so_far = prod
return max_so_far
If num is an integer instead of a string, the following works in a very similar way; only, it starts from the end by iteratively dividing the integer by 10 to get the individual digits; so instead of moving the window to the right, it moves it to the left in each iteration.
def greatest_product(num, window_size):
n = num
prod = after_zero = 1
max_so_far = -1
running_window = deque()
while n:
n, r = divmod(n, 10)
current_window_size = len(running_window)
prev = running_window.popleft() if current_window_size == window_size else 1
if current_window_size == window_size - 1:
prod = after_zero
prod, after_zero, running_window = compute_window(r, prod, after_zero, running_window, prev)
# update max_so_far
if prod > max_so_far:
max_so_far = prod
return max_so_far
Output:
print(greatest_product(num, 13))
# 23514624000
To optimize the code, I think it is better to split the string by "0". Then find the product if the string length is greater than the adjacent number.
number = "73167176531330624919225119674426574742355349194934\
96983520312774506326239578318016984801869478851843\
85861560789112949495459501737958331952853208805511\
12540698747158523863050715693290963295227443043557\
66896648950445244523161731856403098711121722383113\
62229893423380308135336276614282806444486645238749\
30358907296290491560440772390713810515859307960866\
70172427121883998797908792274921901699720888093776\
65727333001053367881220235421809751254540594752243\
52584907711670556013604839586446706324415722155397\
53697817977846174064955149290862569321978468622482\
83972241375657056057490261407972968652414535100474\
82166370484403199890008895243450658541227588666881\
16427171479924442928230863465674813919123162824586\
17866458359124566529476545682848912883142607690042\
24219022671055626321111109370544217506941658960408\
07198403850962455444362981230987879927244284909188\
84580156166097919133875499200524063689912560717606\
05886116467109405077541002256983155200055935729725\
71636269561882670428252483600823257530420752963450"
no_of_adjacent = 13
def find_greatest_product(num, adj):
greatest = 1
for i in range(adj):
greatest *= int(num[i])
prv_temp = greatest
for i in range(adj, len(num)):
temp = (prv_temp // int(num[i-adj])) * int(num[i])
prv_temp = temp
if (temp > greatest):
greatest = temp
return greatest
fractions = number.split("0")
greatest_product = 1
for frac in fractions:
if len(frac) >= no_of_adjacent:
temp = find_greatest_product(frac, no_of_adjacent)
if temp > greatest_product:
greatest_product = temp
print(greatest_product)