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)))
Related
I am trying to solve the arithmetic progression problem from USACO. Here is the problem statement.
An arithmetic progression is a sequence of the form a, a+b, a+2b, ..., a+nb where n=0, 1, 2, 3, ... . For this problem, a is a non-negative integer and b is a positive integer.
Write a program that finds all arithmetic progressions of length n in the set S of bisquares. The set of bisquares is defined as the set of all integers of the form p2 + q2 (where p and q are non-negative integers).
The two lines of input are n and m, which are the length of each sequence, and the upper bound to limit the search of the bi squares respectively.
I have implemented an algorithm which correctly solves the problem, yet it takes too long. With the max constraints of n = 25 and m = 250, my program does not solve the problem in the 5 second time limit.
Here is the code:
n = 25
m = 250
bisq = set()
for i in range(m+1):
for j in range(i,m+1):
bisq.add(i**2+j**2)
seq = []
for b in range(1, max(bisq)):
for a in bisq:
x = a
for i in range(n):
if x not in bisq:
break
x += b
else:
seq.append((a,b))
The program outputs the correct answer, but it takes too long. I tried running the program with the max n/m values, and after 30 seconds, it was still going.
Disclaimer: this is not a full answer. This is more of a general direction where to look for.
For each member of a sequence, you're looking for four parameters: two numbers to be squared and summed (q_i and p_i), and two differences to be used in the next step (x and y) such that
q_i**2 + p_i**2 + b = (q_i + x)**2 + (p_i + y)**2
Subject to:
0 <= q_i <= m
0 <= p_i <= m
0 <= q_i + x <= m
0 <= p_i + y <= m
There are too many unknowns so we can't get a closed form solution.
let's fix b: (still too many unknowns)
let's fix q_i, and also state that this is the first member of the sequence. I.e., let's start searching from q_1 = 0, extend as much as possible and then extract all sequences of length n. Still, there are too many unknowns.
let's fix x: we only have p_i and y to solve for. At this point, note that the range of possible values to satisfy the equation is much smaller than full range of 0..m. After some calculus, b = x*(2*q_i + x) + y*(2*p_i + y), and there are really not many values to check.
This last step prune is what distinguishes it from the full search. If you write down this condition explicitly, you can get the range of possible p_i values and from that find the length of possible sequence with step b as a function of q_i and x. Rejecting sequences smaller than n should further prune the search.
This should get you from O(m**4) complexity to ~O(m**2). It should be enough to get into the time limit.
A couple more things that might help prune the search space:
b <= 2*m*m//n
a <= 2*m*m - b*n
An answer on math.stackexchange says that for a number x to be a bisquare, any prime factor of x of the form 3 + 4k (e.g., 3, 7, 11, 19, ...) must have an even power. I think this means that for any n > 3, b has to be even. The first item in the sequence a is a bisquare, so it has an even number of factors of 3. If b is odd, then one of a+1b or a+2b will have an odd number of factors of 3 and therefore isn't a bisquare.
So here is the deal: I want to (for example) generate 4 pseudo-random numbers, that when added together would equal 40. How could this be dome in python? I could generate a random number 1-40, then generate another number between 1 and the remainder,etc, but then the first number would have a greater chance of "grabbing" more.
Here's the standard solution. It's similar to Laurence Gonsalves' answer, but has two advantages over that answer.
It's uniform: each combination of 4 positive integers adding up to 40 is equally likely to come up with this scheme.
and
it's easy to adapt to other totals (7 numbers adding up to 100, etc.)
import random
def constrained_sum_sample_pos(n, total):
"""Return a randomly chosen list of n positive integers summing to total.
Each such list is equally likely to occur."""
dividers = sorted(random.sample(range(1, total), n - 1))
return [a - b for a, b in zip(dividers + [total], [0] + dividers)]
Sample outputs:
>>> constrained_sum_sample_pos(4, 40)
[4, 4, 25, 7]
>>> constrained_sum_sample_pos(4, 40)
[9, 6, 5, 20]
>>> constrained_sum_sample_pos(4, 40)
[11, 2, 15, 12]
>>> constrained_sum_sample_pos(4, 40)
[24, 8, 3, 5]
Explanation: there's a one-to-one correspondence between (1) 4-tuples (a, b, c, d) of positive integers such that a + b + c + d == 40, and (2) triples of integers (e, f, g) with 0 < e < f < g < 40, and it's easy to produce the latter using random.sample. The correspondence is given by (e, f, g) = (a, a + b, a + b + c) in one direction, and (a, b, c, d) = (e, f - e, g - f, 40 - g) in the reverse direction.
If you want nonnegative integers (i.e., allowing 0) instead of positive ones, then there's an easy transformation: if (a, b, c, d) are nonnegative integers summing to 40 then (a+1, b+1, c+1, d+1) are positive integers summing to 44, and vice versa. Using this idea, we have:
def constrained_sum_sample_nonneg(n, total):
"""Return a randomly chosen list of n nonnegative integers summing to total.
Each such list is equally likely to occur."""
return [x - 1 for x in constrained_sum_sample_pos(n, total + n)]
Graphical illustration of constrained_sum_sample_pos(4, 10), thanks to #FM. (Edited slightly.)
0 1 2 3 4 5 6 7 8 9 10 # The universe.
| | # Place fixed dividers at 0, 10.
| | | | | # Add 4 - 1 randomly chosen dividers in [1, 9]
a b c d # Compute the 4 differences: 2 3 4 1
Use multinomial distribution
from numpy.random import multinomial
multinomial(40, [1/4.] * 4)
Each variable will be distributed as a binomial distribution with mean n * p equal to 40 * 1/4 = 10 in this example.
b = random.randint(2, 38)
a = random.randint(1, b - 1)
c = random.randint(b + 1, 39)
return [a, b - a, c - b, 40 - c]
(I assume you wanted integers since you said "1-40", but this could be easily generalized for floats.)
Here's how it works:
cut the total range in two randomly, that's b. The odd range is because there are going to be at least 2 below the midpoint and at least 2 above. (This comes from your 1 minimum on each value).
cut each of those ranges in two randomly. Again, the bounds are to account for the 1 minimum.
return the size of each slice. They'll add up to 40.
Generate 4 random numbers, compute their sum, divide each one by the sum and multiply by 40.
If you want Integers, then this will require a little non-randomness.
There are only 37^4 = 1,874,161 arrangements of four integers in the range [1,37] (with repeats allowed). Enumerate them, saving and counting the permutations that add up to 40.
(This will be a much smaller number, N).
Draw uniformly distributed random integers K in the interval [0, N-1] and return the K-th permutation. This can easily be seen to guarantee a uniform distribution over the space of possible outcomes, with each sequence position identically distributed. (Many of the answers I'm seeing will have the final choice biased lower than the first three!)
If you want true randomness then use:
import numpy as np
def randofsum_unbalanced(s, n):
# Where s = sum (e.g. 40 in your case) and n is the output array length (e.g. 4 in your case)
r = np.random.rand(n)
a = np.array(np.round((r/np.sum(r))*s,0),dtype=int)
while np.sum(a) > s:
a[np.random.choice(n)] -= 1
while np.sum(a) < s:
a[np.random.choice(n)] += 1
return a
If you want a greater level of uniformity then take advantage of the multinomial distribution:
def randofsum_balanced(s, n):
return np.random.multinomial(s,np.ones(n)/n,size=1)[0]
Building on #markdickonson by providing some control over distribution between the divisors. I introduce a variance/jiggle as a percentage of the uniform distance between each.
def constrained_sum_sample(n, total, variance=50):
"""Return a random-ish list of n positive integers summing to total.
variance: int; percentage of the gap between the uniform spacing to vary the result.
"""
divisor = total/n
jiggle = divisor * variance / 100 / 2
dividers = [int((x+1)*divisor + random.random()*jiggle) for x in range(n-1)]
result = [a - b for a, b in zip(dividers + [total], [0] + dividers)]
return result
Sample output:
[12, 8, 10, 10]
[10, 11, 10, 9]
[11, 9, 11, 9]
[11, 9, 12, 8]
The idea remains to divide the population equally, then randomly move them left or right within the given range. Since each value is still bound to the uniform point we don't have to worry about it drifting.
Good enough for my purposes, but not perfect. eg: the first number will always vary higher, and the last will always vary lower.
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.
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]