I'm solving one programming problem.
Find all numbers x from 1 to n that have a property that digits
of x match digits at the end of x^2.
For example:
5 matches last digit of 5^2 (25)
6 matches last digit of 6^2 (36)
25 matches last digits of 25^2 (625)
76 matches last digits of 76^2 (5776)
376 matches last digits of 376^2 (141376)
625 matches last digits of 625^2 (390625)
etc.
Does anyone know what the numerical criteria is for a number to match itself in last digits of it squared?
I'm programming this in Python and I can only use numeric operations such as /, %, //, *, +, etc.
I can't use str or len or string functions to slice numbers.
Say x has k digits. Then x^2 ends with the digits of x if and only if x^2 - x ends with k zeroes, i.e., if x(x-1) ends with k zeroes.
Count the number of times 2 goes into x and into x-1 (the sum), then do the same for 5. If the min of those is at least as big as the number of digits, you have a winner. If not, you don't.
E.g, Consider 5. 5 goes into 5 once and into 4 zero times, 2 goes into 5 zero times and into 4 twice. The min of 1 and 2 is one. 5 has one digit, so you have a winner.
The easy way to find all such numbers between 1 and n is to just check multiples of powers of 5. Since for each k digit number, you need 5^k to be a factor, check 5^k and all multiples that don't exceed k digits. This could be x or x-1, so you'll also need to check the number above.
So the numbers to check are:
k=1: 5, 6
k=2: 25, 26, 50, 51, 75, 76
k=3: 125, 126, 250, 251, 375, 376, 500, 501, 625, 626, 750, 751, ...
k=4: 625, 626, 1250, 1251, ...
etc...
And all you need to check for any of these is if the min set bit gets you at least k 2s.
Note that 625 shows up for both k=3 and k=4, but that's fine since 0625 is valid. In practice you only need to check once, so you can limit yourself to the multiples which are themselves at least k digits.
Not a complete criteria, but it can be used as a start point:
First: recall that the last digit in a multiplication is the last digit of the multiplication of the last digit of the first operand times the last digit of the second operand.
As the multiplication is of one number by itsef, the possible combinations are: 0x0, 1x1, 2x2, 3x3, 4x4, 5x5, 6x6, 7x7, 8x8, and 9x9.
The multiplications which have the same last digit as its operands are 0x0, 1x1, 5x5 and 6x6.
So you can start by testing only numbers that end in 0, 1, 5 or 6.
For each of these digits, you can build a two digit number, by prepending a digit 1, 2, 3, ..., 9 to them. So, for digit 0, you have 10,20,30,...,90 . You have to find now which of these numbers, multiplied by itsef, produces a result whose next to last digit is the same as it. Let this two digit number be 10a+b where a is the tens and b is the ones. The value of b is already fixed and is one of 0, 1, 5 or 6. Set a value for b and let (10a+b)(10a+b) which is 100a^2 + 20ab + b^2 be the result of multiplying 10a+b by itsef.
We are interested in the tens of this number, so we divide it into 10, resulting 10a^2 + 2ab + b^2/10 and do a 10 modulus to isolate the tens digit. Substitute the value of b in the expression, performing an integer division for the last term. For example, for b=5, the expression would be (10a^2 + 20*5*a + 2) mod 10. Make this expression to be equal to a and you've got an equation that gives you what values of a match the equality.
Hi I wrote this answer:
n=int(input('n='))
for m in range(1, n+1):
d=m**2
x = m
all = 0
all2 = 0
while x != 0:
if x%10 == d%10:
all += 1
x = x//10
d = d//10
all2 += 1
if all == all2:
print m
Please let me know what you think -- it works!
Okay This is the edited version:
LastNum=int(input("Enter end limit: "))
def y(num):
count=0
while num!=0:
num=num//10
count=count+1
return count
def LastDigitMatch(x):
if x%(10**y(x))==(x**2)%(10**y(x)):
return True
else:
return False
print('The required list of numbers: ')
for num in range(1,LastNum+1):
if LastDigitMatch(num):
print(num,end=', ')
I found Giorgi Nakeuri's version (see comment on Dave Galvin's post) the fastest and the simplest among all. I edited the code:
import math
LastNum=int(input("Enter the end limit: "))
print('The required list of numbers:')
for x in range(1,LastNum+1):
if x%(10**math.floor(math.log10(x)+1))==(x**2)%(10**math.floor(math.log10(x)+1)):
print(x,end=', ')
A function which returns a list of these numbers.
def mathcing_squared(n):
return [x for x in range(1, n + 1) if str(x ** 2).endswith(str(x))]
So if n is 650, it will return
[1, 5, 6, 25, 76, 376, 625]
Related
Suppose Alice & Bob have to write from page 1 to page 200. According to this simple division, Alice will write 1, 2, 3... until 100. Bob will write 101, 102, 103... to 200. Bob will have to write a lot more digits than Alice! Let's say Alice & Bob are counters or markers for numbering, so how we can fairly split up this numbering task?
Considering two integers, start & end, for the starting and ending page numbers (inclusive) defining the range of pages that needs handwritten numbering.
A page number has to be written by either Alice or Bob. They cannot jointly write one number.
Page numbers are all decimal integers count from 1. The missing number of pages can start from page 1 or anywhere in the middle of the notes.
Input: There are multiple tests in each test case.
Line 1: Integer N, the number of tests to follow.
Following N lines: Each line has two integers, st ed, for the starting and ending page numbers (inclusive) defining the range of pages that needs handwritten numbering.
#Input examples
4 #N=4 it means 4 following lines
1 200
8 10
9 10
8 11
import sys
import math
n = int(input()) #1 ≤ N ≤ 200
for i in range(n): #1 ≤ start < end ≤ 10,000,000
start, end = [int(j) for j in input().split()]
Output:
N Lines: For the test in the input, It should be written the corresponding last page number that should be responsible by Alice to write the needed page number on.
#Output examples
118
9
9
9
I was trying to get inspired by this post on fair casting dice unsuccessfully. I also was wondering the solution is far from Checking number of elements for Counter
.
First thing to note is this cannot be done, consider the sequence 99, 100. You cannot split this up fairly. In saying that you can get pretty close +- 1 digit, this assumes you always start counting from 1.
start = 1
end = 200
bobs_numbers = []
alices_numbers = []
count = 0
for i in range(end, start - 1, -1):
if count > 0:
bobs_numbers.append(i)
count -= len(str(i))
else:
alices_numbers.append(i)
count += len(str(i))
print(bobs_numbers, alices_numbers, count)
This is an answer to the initial question. Since the question has been changed, I posted another answer for the new question.
The initial question was: Partition the set [1, 200] into two subsets such that the total number of digits in one subset is as close to possible to the total number of digits in the other subset.
Since user Mitchel Paulin already gave a straightforward iterative solution, let me give an arithmetic solution.
Counting the number of digits
First, let's count the total number of digits we want to split between Alice and Bob:
there are 9 numbers with 1 digit;
there are 90 numbers with 2 digits;
there are 101 numbers with 3 digits.
Total: 492 digits.
We want to give 246 digits to Alice and 246 digits to Bob.
How to most simply get 246 digit by summing up numbers with 1, 2 and 3 digits?
246 = 3 * 82.
Let's give 82 numbers with 3 digits to Bob, and all the other numbers to Alice.
Finally Bob can handle numbers [119, 200] and Alice can handle numbers [1, 118].
Generalizing to any range [1, n]
Counting the numbers of numbers with each possible number of digits should be O(log n).
Dividing by 2 to get the number of digits for Bob is O(1).
Decomposing this number using dynamic programming is linear in the maximum number of digits, i.e., O(log n) space and time (this is exactly the coin change problem).
Transforming this decomposition into a union of ranges is straightforward, and linear in the maximum number of digits, so again O(log n). Deducing the ranges for Alice by "subtracting" Bob's ranges from [1, n] is also straightforward.
Conclusion: the algorithm is O(log n) space and time, as opposed to Mitchel Paulin's O(n) algorithm. The output is also logarithmic instead of linear, since it can be written as a union of ranges, instead of a long list.
This algorithm is a bit more complex to write, but the output being in the form of ranges mean that Alice and Bob won't bother each other too much by writing adjacent pages, which they would do a lot with the simpler algorithm (which mostly alternates between giving a number to Bob and giving a number to Alice).
Since the question has changed, this is an answer the new question.
The new question is: Given a range [a, b], find number m such that the total number of digits in range [a, m] is as close as possible to the number of digits in range [m+1, b].
Algorithm explanation
The algorithm is simple: Start with m = (a + b) / 2, count the digits, then move m to the right or to the left to adjust.
To count the total number of digits in a range [1, n], we first count the number of unit digits (which is n); then add the number of tens digits (which is n - 9; then add the number of hundreds digits (which is n - 99); etc.
To count the total number of digits in a range [a, b], we take the difference between the total number of digits in ranges [1, b] and [1, a-1].
Note that the number of digits of a given number n > 1 is given by any of the two expressions math.ceil(math.log10(n)) and len(str(n)). I used the former in the code below. If you have a phobia of logarithms, you can replace it with the latter; in which case import math is no longer needed.
Code in python
import math
def count_digits_from_1(n):
power_of_ten = math.ceil(math.log10(n))
total_digits = 0
for i in range(1, power_of_ten+1):
digits_at_pos_i = n - (10**(i-1) - 1)
total_digits += digits_at_pos_i
return total_digits
def count_digits(a, b):
if a > 2:
return count_digits_from_1(b) - count_digits_from_1(a-1)
else:
return count_digits_from_1(b) - (a - 1) # assumes a >= 1
def share_digits(a, b):
total_digits = count_digits(a, b)
m = (a + b) // 2
alices_digits = count_digits(a, m)
bobs_digits = total_digits - alices_digits
direction = 1 if alices_digits < bobs_digits else -1
could_be_more_fair = True
while (could_be_more_fair):
new_m = m + direction
diff = math.ceil(math.log10(new_m))
new_alices_digits = alices_digits + direction * diff
new_bobs_digits = bobs_digits - direction * diff
if abs(alices_digits - bobs_digits) > abs(new_alices_digits - new_bobs_digits):
alices_digits = new_alices_digits
bobs_digits = new_bobs_digits
m = new_m
else:
could_be_more_fair = False
return ((a, m), (m+1, b))
if __name__=='__main__':
for (a, b) in [(1, 200), (8, 10), (9, 10), (8, 11)]:
print('{},{} ---> '.format(a,b), end='')
print(share_digits(a, b))
Output:
1,200 ---> ((1, 118), (119, 200))
8,10 ---> ((8, 9), (10, 10))
9,10 ---> ((9, 9), (10, 10))
8,11 ---> ((8, 10), (11, 11))
Remark: This code uses the assumption 1 <= a <= b.
Performance analysis
Function count_digits_from1 executes in O(log n); its for loop iterates over the position of the digits to count the number of unit digits, then the number of tens digits, then the number of hundreds digits, etc. There are log10(n) positions.
The question is: how many iterations will the while loop in share_digits have?
If we're lucky, the final value of m will be very close to the initial value (a+b)//2, so the number of iterations of this loop might be O(1). This remains to be proven.
If the number of iterations of this loop is too high, the algorithm could be improved by getting rid of this loop entirely, and calculating the final value of m directly. Indeed, replacing m with m+1 or m-1 changes the difference abs(alices_digits - bobs_digits) by exactly two times the number of digits of m+1 (or m-1). Therefore, the final value of m should be given approximately by:
new_m = m + direction * abs(alices_digits - bobs_digits) / (2 * math.ceil(math.log10(m)))
I was reviewing the topic of recursion in a tutorial site and came across the problem and solution below:
Problem:
Given an integer, create a function which returns the sum of all the individual digits in that integer. For example: if n = 4321, return 10 since 4+3+2+1=10.
Solution:
def sum_func(n):
if n<10:
return n
else:
return n%10 + sum_func(int(n/10))
pass
I understand the "if n<10" is the base case - if n is single digit, then just return n. What I couldn't wrap my head around is the "n%10 + sum_func(int(n/10))" line of code. Assume n is 235, n%10 will equal 5 and int(n/10) will equal 23. Then ultimately 5 + 23 will become 28 and not 10.
Could someone please help me understand what "n%10 + sum_func(int(n/10))" does? If you could walk through the logic one line at a time that would be greatly appreciated!
if n = 235 then int(n/10) is 23. However, you do not add 5 and 23. You add sum_func(int(n/10)) and 5.
sum_func(int(n/10)) is not int(n/10)
sum_func(int(n/10)) adds the digits in the number "23" together.
As such, sum_func(int(n/10)) == 2 + 3 == 5
sum_func(235) == sum_func(235)
== sum_func(23) + 5
== sum_func(2) + 3 + 5
== 2 + 3 + 5
As you say if there's only 1 digit return it.
The % is the modulus operator - i.e. the remainder when dividing by 10, or simply the last digit (i.e. 235%10 = 5)
int(n/10) drops the last digit since the int() function rounds down - i.e. 235 -> 23
Now what you seem to be confused by is within sum_func it calls sum_func again with the remainder once the last digit has been dropped i.e. 23 (this is the recursion part) which will then return 5.
i.e. you have
sum_func(235)
=5 + sum_func(23)
=5 + (3 + sum_func(2))
=5 + (3 + (2))
=10
I wrote code to solve the following problem, however it fails on the last two test cases. My logic used to solve the problem seems sound, and even after having a coworker review it, we both cannot figure out why it would work for the first 8 test cases but not the last two (which were randomly generated).
Problem:
Given a string, return the position of the inputted string in a
alphabetically sorted list of all the possible permutations of the
characters in that string. For example, the permutations of ABAB are
[AABB,ABAB,ABBA,BAAB,BABA,BBAA] where the position of ABAB in that
list is 2.
Logic I used to solve the problem:
For larger inputs, it is impossible (inefficient) to generate a list of permutations, so the point is to find the position without generating the alphabetical list. This can be done by finding the frequency of the characters. For the above example, the first charcater in ABAB is A, so before = 0 and after = .5, and between = 6, so you decrease max by .5*6 which is 3, for a minn of 1 and maxx of 3, to leave only [AABB,ABAB,ABBA], the perms with A as the first character! Then the characters left are BAB. minn = 1 and maxx = 3, and between = 3. So for B, before would be .33 and after would be 0, so increase minn by 3*.33 for a minn of 2 and maxx of 3, which equals [ABAB,ABBA] the perms that have AB as the first two characters! Keep doing that for every character and it will find the input in the list.
My Code:
## Imports
import operator
from collections import Counter
from math import factorial
from functools import reduce
## Main function, returns list position
def listPosition(word):
#turns string into list of numbers, A being 1, B being 2, and so on
val = [ord(char) - 96 for char in word.lower()]
#the result has to be between 1 and the number of permutations
minn = 1
maxx = npermutations(word)
#so we just increase the min and decrease the max based on the sum of freq
#of the characters less than and greater than each character
for indx in range(len(word)):
between = (maxx+1-minn)
before,after = sumfreq(val[indx:],val[indx])
minn = minn + int(round((between * before),0))
maxx = maxx - int(between * after)
return maxx #or minn, doesn't matter. they're equal at this point
## returns the number of permutations for the string (this works)
def npermutations(word):
num = factorial(len(word))
mults = Counter(word).values()
den = reduce(operator.mul, (factorial(v) for v in mults), 1)
return int(num / den)
## returns frequency as a percent for the character in the list of chars
def frequency(val,value):
f = [val.count(i)/len(val) for i in val]
indx = val.index(value)
return f[indx]
#returns sum of frequencies for all chars < (before) and > (after) the said char
def sumfreq(val,value):
before = [frequency(val,i) for i in [i for i in set(val) if i < value]]
after = [frequency(val,i) for i in [i for i in set(val) if i > value]]
return sum(before),sum(after)
tests= ['A','ABAB','AAAB','BAAA','QUESTION','BOOKKEEPER','ABCABC','IMMUNOELECTROPHORETICALLY','ERATXOVFEXRCVW','GIZVEMHQWRLTBGESTZAHMHFBL']
print(listPosition(tests[0]),"should equal 1")
print(listPosition(tests[1]),"should equal 2")
print(listPosition(tests[2]),"should equal 1")
print(listPosition(tests[3]),"should equal 4")
print(listPosition(tests[4]),"should equal 24572")
print(listPosition(tests[5]),"should equal 10743")
print(listPosition(tests[6]),"should equal 13")
print(listPosition(tests[7]),"should equal 718393983731145698173")
print(listPosition(tests[8]),"should equal 1083087583") #off by one digit?
print(listPosition(tests[9]),"should equal 5587060423395426613071") #off by a lot?
You can use logic that only requires integer arithmetic. First, create the lexicographically first permutation:
BOOKKEEPER -> BEEEKKOOPR
Then, for each letter, you can calculate how many unique permutations it took to move it to its place. Since the first letter B is already in place, we can ignore it and look at the rest of the letters:
B EEEKKOOPR (first)
B OOKKEEPER (target)
To know how many permutations it takes to bring the O to the front, we calculate how many unique permutations there are with the E in front, then with the K in front:
E+EEKKOOPR -> 8! / (2! * 2! * 2!) = 40320 / 8 = 5040
K+EEEKOOPR -> 8! / (3! * 2!) = 40320 / 12 = 3360
Where 8 is the number of letters to be permuted, and 2 and 3 are the number of multiples of the letters. So after 8400 permutations we are at:
BO EEEKKOPR
Now, again, we calculate how many permutations it takes to bring the second O to the front:
E+EEKKOPR -> 7! / (2! * 2!) = 5040 / 4 = 1260
K+EEEKOPR -> 7! / (3!) = 5040 / 6 = 840
So after 10500 permutations we are at:
BOO EEEKKPR
Then we calculate how many permutations it takes to bring the K to the front:
E+EEKKPR -> 6! / (2! * 2!) = 720 / 4 = 180
So after 10680 permutations we are at:
BOOK EEEKPR
Then we calculate how many permutations it takes to bring the second K to the front:
E+EEKPR -> 5! / 2! = 120 / 2 = 60
So after 10740 permutations we are at:
BOOKK EEEPR
The next two letters are already in place, so we can skip to:
BOOKKEE EPR
Then we calculate how many permutations it takes to get the P to the front:
E+PR -> 2! = 2
So after 10742 permutations we are at:
BOOKKEEP ER
And the last two letters are also already in order, so the answer is 10743 (add 1 because the 1-based index is asked).
As #rici points out this is a floating point error (see Is floating point math broken?). Fortunately python has fractions.
A couple of judicious uses of fractions.Fraction fixes the issue without changing the body of the code, e.g.:
from fractions import Fraction
...
## returns the number of permutations for the string (this works)
def npermutations(word):
num = factorial(len(word))
mults = Counter(word).values()
den = reduce(operator.mul, (factorial(v) for v in mults), 1)
return int(Fraction(num, den))
## returns frequency as a percent for the character in the list of chars
def frequency(val,value):
f = [Fraction(val.count(i),len(val)) for i in val]
indx = val.index(value)
return f[indx]
...
In []:
print(listPosition(tests[0]),"should equal 1")
print(listPosition(tests[1]),"should equal 2")
print(listPosition(tests[2]),"should equal 1")
print(listPosition(tests[3]),"should equal 4")
print(listPosition(tests[4]),"should equal 24572")
print(listPosition(tests[5]),"should equal 10743")
print(listPosition(tests[6]),"should equal 13")
print(listPosition(tests[7]),"should equal 718393983731145698173")
print(listPosition(tests[8]),"should equal 1083087583")
print(listPosition(tests[9]),"should equal 5587060423395426613071")
Out[]:
1 should equal 1
2 should equal 2
1 should equal 1
4 should equal 4
24572 should equal 24572
10743 should equal 10743
13 should equal 13
718393983731145698173 should equal 718393983731145698173
1083087583 should equal 1083087583
5587060423395426613071 should equal 5587060423395426613071
Updated
Based on #m69's excellent explanation here's a much simpler implementation:
from math import factorial
from collections import Counter
from functools import reduce
from operator import mul
def position(word):
charset = Counter(word)
pos = 1 # Per OP 1 index
for letter in word:
chars = sorted(charset)
for char in chars[:chars.index(letter)]:
ns = Counter(charset) - Counter([char])
pos += factorial(sum(ns.values())) // reduce(mul, map(factorial, ns.values()))
charset -= Counter([letter])
return pos
Which gives the same results as above:
In []:
tests = ['A', 'ABAB', 'AAAB', 'BAAA', 'QUESTION', 'BOOKKEEPER', 'ABCABC',
'IMMUNOELECTROPHORETICALLY', 'ERATXOVFEXRCVW', 'GIZVEMHQWRLTBGESTZAHMHFBL']
print(position(tests[0]),"should equal 1")
print(position(tests[1]),"should equal 2")
print(position(tests[2]),"should equal 1")
print(position(tests[3]),"should equal 4")
print(position(tests[4]),"should equal 24572")
print(position(tests[5]),"should equal 10743")
print(position(tests[6]),"should equal 13")
print(position(tests[7]),"should equal 718393983731145698173")
print(position(tests[8]),"should equal 1083087583")
print(position(tests[9]),"should equal 5587060423395426613071")
Out[]:
1 should equal 1
2 should equal 2
1 should equal 1
4 should equal 4
24572 should equal 24572
10743 should equal 10743
13 should equal 13
718393983731145698173 should equal 718393983731145698173
1083087583 should equal 1083087583
5587060423395426613071 should equal 5587060423395426613071
Given an n-digit positive integer, count and print the number of sub-sequences formed by concatenating the given number's digits that are divisible by 8. As the result can be large, print the result modulo (10**9 + 7).
the question can be read here
Looks simple and i tried to solve it. My approach, tried to keep track of all 3 digit, 2 digit and 1 digit numbers divisible by 8. since any number with last 3 digits divisible by 8 is exactly divisible by 8, i tired to keep a tree for 3 digit and track occurrences (2**p, p=number of preceding characters gives number of sequences), but this was not right since i could not handle some scenarios where digits are interleaved (example 9868).
After the contest is over, i was looking for elegant solution since the Editorial was not clear enough. I came across one, but could not understand whats going on. Could someone explain the math behind the following solution ?
#!/bin/python3
import sys
# n = int(input().strip())
# number = input().strip()
n = 10
# number = "0123456789" #81
number = "1234567890" #109
# n = 5
# number = "9868" #5
mods = [0, 0, 0, 0, 0, 0, 0, 0]
P = 10**9 + 7
for k in number:
m = int(k) % 8
new_mods = mods[:]
new_mods[m] += 1
for i in range(8):
new_index = (i*10+m)%8
new_mods[new_index] = (new_mods[new_index] + mods[i]) % P
mods = new_mods
print(mods[0])
ideone link
The idea behind the algorithm is to incrementally process prefixes of the given number and for every prefix and every r from 0 to 7 to calculate the number of subsequences of that prefix that form a number equal to r modulo 8.
Let [n₀n₁...nₓ] be the input number.
Let S(i, r) be the number of subsequences of the prefix [n₀n₁...nᵢ] that form a number equal to r modulo 8.
In order to calculate S(i+1, r) for 0 ≤ r ≤ 7 we only need to know S(i, r) for 0 ≤ r ≤ 7. That's what makes it possible to process the digits one by one in linear time.
There are 3 kinds of subsequences of [n₀n₁...nᵢ₊₁]:
Subsequences that don't contain nᵢ₊₁.
For every subsequence [a...b] of [n₀n₁...nᵢ] there is an equal subsequence of [n₀n₁...nᵢ₊₁].
We take them into account by adding S(i, r) to S(i+1, r) for every r from 0 to 7.
Subsequences that contain nᵢ₊₁ and at least one digit before it.
For every subsequence [a...b] of [n₀n₁...nᵢ] that is equal to r modulo 8 there is a subsequence [a...bnᵢ₊₁] of [n₀n₁...nᵢ₊₁] that is equal to (r*10+nᵢ₊₁)%8 modulo 8 (since [a...bnᵢ₊₁] = 10*[a...b] + nᵢ₊₁).
We take them into account by adding S(i, r) to S(i+1, (r*10+nᵢ₊₁)%8) for every r from 0 to 7.
One-digit subsequence [nᵢ₊₁].
We take it into account by adding 1 to S(i+1, nᵢ₊₁ % 8).
In the code you quoted:
S(i, 0), ..., S(i, 7) is mods — the array of 8 numbers for the previous prefix.
S(i+1, 0), ..., S(i+1, 7) is new_mods — the array of 8 numbers for the current prefix.
nᵢ₊₁ is k — the last digit of the current prefix.
Here is a simple program to find the last several non-zero digits of the product of the numbers 1 through 105, removing trailing zeros along the way:
def f(a, b):
s = 1
for i in range(a, b+1):
s *= i
while not s % 10:
s //= 10
s = s % 10**10
return s
f(1, 10**5) and f(10**5+1, 2*10**5) do not produce the same last 5 digits, though mathematically they should.
Additionally, f(1, 10**5)**10 does not produce the same ending digits as f(1, 10**6).
Where is the problem here and what is the correct implementation?
Your code correctly finds the last ten digits after the rightmost zero digits are discarded. Your belief that the code is incorrect rests on a pair of fallacious claims.
First you claim that the product of 1 through n, or n!, should have the same non-zero digits as the product of n+1 through 2n, which is:
(n+1)*(n+2)*...*(2n) = (2n)! / n!
In saying that the non-zero digits of n! and (2n)!/n! should be equal, you are implying that for some constant k, we have:
10^k * n! = (2n)! / n!
But this is false in general. Consider this counterexample:
20! = 2432902008176640000
40! / 20! = 335367096786357081410764800000
Your second claim is that n! raised to the power of 10 is the same as (10n)!. This is false. In general, it is not true that:
(n!)^k = (kn)!
Counterexample:
3!^10 = 60466176
30! = 265252859812191058636308480000000
I generated these figures with the following function:
def series(start, end):
x = start
for i in range(start + 1, end + 1):
x *= i
return x
For example, to see that the product of 1 through 100 does not have the same non-zero digits as the product of 101 through 200, execute:
print(series(1, 100))
print(series(101, 200))
The first yields a number whose last five digits after removing the rightmost zeros are 16864. For the second, they are 02048.