I need to find all the combinations flowers. Number of flowers only odd. Purchase amount not greater than the predetermined.
def bouquets(narcissus_price, tulip_price, rose_price, summ):
count_combinations = 0
for n in xrange(int(summ / narcissus_price) + 1):
for t in xrange(int(summ / tulip_price) + 1):
if n * narcissus_price + t * tulip_price > summ:
break
for r in xrange([1,0][(n + t) % 2], int(summ / rose_price) + 1, 2):
if n * narcissus_price + t * tulip_price + r * rose_price > summ:
break
elif (n + t + r) & 1:
count_combinations += 1
return count_combinations
print bouquets(200, 300, 400, 100000) == 3524556 # large run-time
Reduce the iteration range for tulips - rather than iterating to summ // tulip_price you can stop at (summ - n * narcissus_price) // tulip_price
You can count the number of possible values for r without enumerating them
Example:
def bouquets(n_price, t_price, r_price, total_price):
"""
Count how many n, t, r (integers >= 0) exist
such that
n * n_price + t * t_price + r * r_price <= total_price
and
n + t + r is odd
"""
count = 0
max_n = total_price // n_price
for n in xrange(max_n + 1):
rem_n = total_price - n * n_price
max_t = rem_n // t_price
for t in xrange(max_t + 1):
rem_t = rem_n - t * t_price
max_r = rem_t // r_price
min_r = (n + t + 1) % 2
count += (max_r - min_r) // 2 + 1
return count
On the given test input, this reduces the runtime from 2.33s to 67.2ms (about 35 times faster).
Related
Hi I'm trying to understand why the time complexity of the next function:
def f(n):
result = 0
jump = 1
cur = 0
while cur < n:
result += cur
if jump*jump < n:
jump *= 2
cur += jump
return result
is O(√n). I understand that the code under the if statement inside the function gets executed until jump >= √n, I also noticed that cur = 1 + 2 + 4 + 8 + 16 + ... but I still can't get the answer.
A little math is needed here.
Suppose that jump^2 is greater than or equal to n after m iterations, and then the jump will not be doubled again. Here we have:
jump = 2^m >= √n
At this time, cur is:
cur = 1 + 2 + 4 + ... + 2^m = 2 ^ (m + 1) - 1
Then, our total number of iterations will not be greater than:
n - (2 ^ (m + 1) - 1)
m + ceil( --------------------- )
2^m
Because we have 2^m >= √n and 2 ^ (m - 1) < √n, so
n - (2 ^ (m + 1) - 1)
m + ceil( --------------------- )
2^m
n - 2√n + 1
< (log √n + 1) + (----------- + 1)
2 √n
n + 1
= log √n + -----
2 √n
Here, it is clear that (n + 1) / √n is O(√n), and the logarithm is also O(√n), so the sum of the two is O(√n).
Because it is about time complexity, we can prove it more loosely. For example, before jump reaches √n, it is O(log_2 √n) complexity, and after that, it is O(√n) complexity. The sum of the two is obviously O(√n) complexity.
Then the sum and the last added number and the number of numbers added must be printed.
I am currently stuck, I managed to get the sum part working. The last added number output is printed "23" but should be "21". And lastly, how can I print the number of numbers added?
Output goal: 121, 21, 11
Here is my code:
n = int()
sum = 0
k = 1
while sum <= 100:
if k%2==1:
sum = sum + k
k = k + 2
print('Sum is:', sum)
print("last number:", k)
Edit: Would like to thank everyone for their help and answers!
Note, that (you can prove it by induction)
1 + 3 + 5 + ... + 2 * n - 1 == n**2
<----- n items ----->
So far so good in order to get n all you have to do is to compute square root:
n = sqrt(sum)
in case of 100 we can find n when sum reach 100 as
n = sqrt(100) == 10
So when n == 10 then sum == 100, when n = 11 (last item is 2 * n - 1 == 2 * 11 - 1 == 21) the sum exceeds 100: it will be
n*n == 11 ** 2 == 121
In general case
n = floor(sqrt(sum)) + 1
Code:
def solve(s):
n = round(s ** 0.5 - 0.5) + 1;
print ('Number of numbers added: ', n);
print ('Last number: ', 2 * n - 1)
print ('Sum of numbers: ', n * n)
solve(100)
We have no need in loops here and can have O(1) time and space complexity solution (please, fiddle)
More demos:
test : count : last : sum
-------------------------
99 : 10 : 19 : 100
100 : 11 : 21 : 121
101 : 11 : 21 : 121
Change your while loop so that you test and break before the top:
k=1
acc=0
while True:
if acc+k>100:
break
else:
acc+=k
k+=2
>>> k
21
>>> acc
100
And if you want the accumulator to be 121 just add k before you break:
k=1
acc=0
while True:
if acc+k>100:
acc+=k
break
else:
acc+=k
k+=2
If you have the curiosity to try a few partial sums, you immediately recognize the sequence of perfect squares. Hence, there are 11 terms and the last number is 21.
print(121, 21, 11)
More seriously:
i, s= 1, 1
while s <= 100:
i+= 2
s+= i
print(s, i, (i + 1) // 2)
Instead of
k = k + 2
say
if (sum <= 100):
k = k +2
...because that is, after all, the circumstance under which you want to add 2.
To also count the numbers, have another counter, perhasp howManyNumbers, which starts and 0 and you add 1 every time you add a number.
Just Simply Change you code to,
n = int()
sum = 0
k = 1
cnt = 0
while sum <= 100:
if k%2==1:
sum = sum + k
k = k + 2
cnt+=1
print('Sum is:', sum)
print("last number:", k-2)
print('Number of Numbers Added:', cnt)
Here, is the reason,
the counter should be starting from 0 and the answer of the last printed number should be k-2 because when the sum exceeds 100 it'll also increment the value of k by 2 and after that the loop will be falls in false condition.
You can even solve it for the general case:
def sum_n(n, k=3, s =1):
if s + k > n:
print('Sum is', s + k)
print('Last number', k)
return
sum_n(n, k + 2, s + k)
sum_n(int(input()))
You can do the following:
from itertools import count
total = 0
for i, num in enumerate(count(1, step=2)):
total += num
if total > 100:
break
print('Sum is:', total)
print("last number:", 2*i + 1)
To avoid the update on k, you can also use the follwoing idiom
while True:
total += k # do not shadow built-in sum
if total > 100:
break
Or in Python >= 3.8:
while (total := total + k) <= 100:
k += 2
Based on your code, this would achieve your goal:
n = 0
summed = 0
k = 1
while summed <= 100:
n += 1
summed = summed + k
if summed <= 100:
k = k + 2
print(f"Sum is: {summed}")
print(f"Last number: {k}")
print(f"Loop count: {n}")
This will solve your problem without changing your code too much:
n = int()
counter_sum = 0
counter = 0
k = 1
while counter_sum <= 100:
k+= 2
counter_sum =counter_sum+ k
counter+=1
print('Sum is:', counter_sum)
print("last number:", k)
print("number of numbers added:", counter)
You don't need a loop for this. The sum of 1...n with step size k is given by
s = ((n - 1) / k + 1) * (n + 1) / k
You can simplify this into a standard quadratic
s = (n**2 - k * n + k - 1) / k**2
To find integer solution for s >= x, solve s = x and take the ceiling of the result. Apply the quadratic formula to
n**2 - k * n + k - 1 = k**2 * x
The result is
n = 0.5 * (k + sqrt(k**2 - 4 * (k - k**2 * x - 1)))
For k = 2, x = 100 you get:
>>> from math import ceil, sqrt
>>> k = 2
>>> x = 100
>>> n = 0.5 * (k + sqrt(k**2 - 4 * (k - k**2 * x - 1)))
>>> ceil(n)
21
The only complication arises when you get n == ceil(n), since you actually want s > x. In that case, you can test:
c = ceil(n)
if n == c:
c += 1
import sys
t=(int(sys.stdin.readline()))
for i in range(0,t):
n=int(sys.stdin.readline())
c=0
s=n*(n+1)/2
if s%2!=0:
print(0)
else:
c=0
i=-1
a=[i for i in range(1,n+1)]
h=s//2
m=0
s1=0
for i in range(n-1,-1,-1):
s1+=a[i]
c+=1
if s1==h:
m=1
break
if s1>h:
break
if m==1:
s1=((c+1)*(2+((c-1)-1)))//2+((n-c-1)*(2+((n-c-1)-1)))//2
print(s1)
else:
print(c)
I am new to python , How can i write this code with using for loop? i don't want to use for loop because i get TLE error. Thanks in advance
Here is the question :
N. Consider the sequence sequence=(1,2,…,N). You should choose two elements of this sequence and swap them.
A swap is perfect if there is an integer o (1≤o<N) such that the sum of the first M elements of the resulting sequence is equal to the sum of its last N−o elements. Find the number of perfect swaps.
i got interested in the problem and found this so far:
a slow version that creates list and really does swap elements is this:
from itertools import combinations
def slow(N):
found = 0
for i, j in combinations(range(N), 2):
lst = list(range(1, N + 1))
lst[i], lst[j] = lst[j], lst[i]
for m in range(1, N):
a = m * (m + 1) // 2
b = (N - m) * (N + m + 1) // 2
if i < m <= j:
a = a - i + j
b = b - j + i
assert a == sum(lst[:m])
assert b == sum(lst[m:])
if sum(lst[:m]) == sum(lst[m:]):
found += 1
if i < m <= j:
assert 2 * m * (m + 1) + 4 * j == N * (N + 1) + 4 * i
else:
assert 2 * m * (m + 1) == N * (N + 1)
else:
if i < m <= j:
assert 2 * m * (m + 1) + 4 * j != N * (N + 1) + 4 * i
else:
assert 2 * m * (m + 1) != N * (N + 1)
return found
as you see i found criteria the indices have to fulfill in order for the sum to be correct:
if i < m <= j:
assert 2 * m * (m + 1) + 4 * j == N * (N + 1) + 4 * i
else:
assert 2 * m * (m + 1) == N * (N + 1)
i also found the direct formula to calculate the sum up to m and the one starting from m:
a = m * (m + 1) // 2
b = (N - m) * (N + m + 1) // 2
if i < m <= j:
a = a - i + j
b = b - j + i
all of that can can be calculated using some basic mathematics.
starting from that you can do some more maths and see that there are 2 cases to consider:
there is an m such that the sum of the original list [1, 2, 3, ..., m, m+1, ..., N] up to m equals the sum of the rest of the list (e.g. N = 20; m = 14). two cases again:
all the swaps that do not cross the m boundary are valid (there are comb(m, 2) + comb((N - m), 2)) of them.
when you split at m-1 you will find more swaps; this time you have to swap accross the m-1 boundary.
the m in that case is calculated from
m = - 1 + sqrt(1 + 2 * N * (N + 1)) / 2
the calculation for m in the first case is not an integer (i.e. 1 + 2 * N * (N + 1) is not a perfect square). the m to consider is then then the floor of the result of the formula above (i use int instead of math.floor). two cases again for the difference of the sum of the two splits:
the difference is even: there are more swaps that need to go over the m boundary.
the difference is odd: no additional swaps (swapping will always result in an even difference)
this is the code:
from math import sqrt, comb
def fast(N):
found = 0
arg = (1 + 2 * N * (N + 1))
sq = round(sqrt(arg))
if sq ** 2 == arg and sq & 1:
m = (-1 + sq) // 2
found += comb(m, 2) + comb((N - m), 2)
m -= 1
found += N - m - 1
else:
m = int((-1 + sqrt(arg)) // 2)
diff = ((m + 1 + N) * (N - m) - m * (m + 1)) // 2
if diff & 1 == 0:
found += N - m
return found
I was trying to solve Leetcode#279.Perfect-Squares
When I tried i ** 2 in loops, I got Time Limit Exceed. But once I change it to i * i, the code was accepted, that means i * i is faster than i ** 2 in python
What principles in python3 caused this difference?
Code and result for reference:
Use j * j, AC, beats 23%
class Solution:
def numSquares(self, n: int):
if n < 2:
return n
dp = [n] * (n + 1)
dp[0] = 0
dp[1] = 1
for i in range(2, n + 1):
j = 1
while j * j <= i:
dp[i] = min(dp[i], dp[i - j * j] + 1)
j += 1
return dp[-1]
If change all j * j to j ** 2, TLE.
a matrix consists of N × N blocks .the block number is equal to the sum of the row number and the column number. each block consists of data, and data is equal to difference of sum of even and odd digits of the block number . calculate total data of n*n blocks
i/o format
lets n = 4
so
matrix will be
2 3 4 5
3 4 5 6
4 5 6 7
5 6 7 8
so total data = 2+3+4+5+3+4+5+6+4+5+6+7+5+6+7+8=80
if number of block is 4256 in any case then data in it will be abs(diff(sum(even digits)- sum(odd digits))) which is abs((4+2+6)-(5))= 7
my naive attempt
n = int(raw_input())
sum1=0
sum2=0
for i in range(1,n+1):
for j in range(1,n+1):
sum1 = i+j
diffsum = diff(sum1)
sum2 = sum2+diffsum
print sum2
again optimized attempt
def diff(sum1):
sum1 = str(sum1)
m = sum([int(i) for i in sum1 if int(i) % 2 == 0])
f = sum([int(i) for i in sum1 if int(i) % 2 != 0])
return abs(m - f)
n = int(raw_input())
sum1 = 0
k = 1
# t1 = time.time()
p = 2 * n
for i in range(2, n + 2):
diffsum = diff(i)
diffsum1 = diff(p)
sum1 = sum1 + (diffsum * k)
sum1 = sum1 + (diffsum1 * k)
p = p - 1
k = k + 1
sum1 = sum1 - (diff(n + 1) * n)
print sum1
diff is common function in both case. i need more optmization with the following algorithm
Your optimised approach calculates the digit sum only once for each number, so at first sight, there isn't anything to be gained from memoisation.
You can improve the performance of your diff function by merging the two loops into one and use a dictionary to look up whether you add or subtract a digit:
value = dict(zip("0123456789", (0, -1, 2, -3, 4,-5, 6,-7, 8,-9)))
def diff2(s):
s = str(s)
return abs(sum([value[i] for i in s]))
This will require a conversion to string. You can get a bit faster (but not much) by calculating the digits by hand:
dvalue = [0, -1, 2, -3, 4,-5, 6,-7, 8,-9]
def diff(s):
t = 0
while s:
t += dvalue[s % 10]
s //= 10
return abs(t)
Finally, you can make use of the fact that you calculate all digit sums from 2 up to 2·n sequentially. Store the digits of the current number in an array, then implement an odometer-like counter. When you increment that counter, keep track of the odd and even digit sums. In 9 of 10 cases, you just have to adjust the last digit by removing its value from the respective sum and by adding the next digit to the other sum.
Here's a program that does this. The function next increments the counter and keeps the digit sums of even and odd numbers in sums[0] and sums[1]. The main program is basically the same as yours, except that the loop has been split into two: One where k increases and one where it decreases.
even = set(range(0, 10, 2))
def next(num, sums):
o = num[0]
if o in even:
sums[0] -= o
sums[1] += o + 1
else:
sums[0] += o + 1
sums[1] -= o
num[0] += 1
i = 0
while num[i] == 10:
sums[0] -= 10
num[i] = 0
i += 1
o = num[i]
if o in even:
sums[0] -= o
sums[1] += o + 1
else:
sums[0] += o + 1
sums[1] -= o
num[i] += 1
n = int(raw_input())
total = 0
m = len(str(2 * n + 1))
num = [0] * m
num[0] = 2
sums = [2, 0]
k = 1
for i in range(2, n + 2):
total += abs(sums[0] - sums[1]) * k
k += 1
next(num, sums)
k = n
for i in range(n + 2, 2*n + 1):
k -= 1
total += abs(sums[0] - sums[1]) * k
next(num, sums)
print total
I've said above that memoisation isn't useful for this approach. That's not true. You could store the even and odd digit sums of number i and make use of it when calculating the numbers 10 * i to 10 * i + 9. When you call diff in order of increasing i, you will have access to the stored sums of i // 10.
This isn't significantly faster than the odometer approach, but the implementation is clearer at the cost of additional memory. (Preallocated arrays work better than dictionaries for big n. You don't need to reserve space for numbers above (2*n + 11) / 10.)
def diff(s):
d = s % 10
e = ememo[s / 10]
o = omemo[s / 10]
if d in even:
e += d
else:
o += d
if s < smax:
ememo[s] = e
omemo[s] = o
return e, o
n = int(raw_input())
total = 0
even = set(range(0, 10, 2))
smax = (2*n + 11) / 10
omemo = smax * [0]
ememo = smax * [0]
omemo[1] = 1
k = 1
for i in range(2, n + 2):
e, o = diff(i)
total += abs(e - o) * k
k += 1
k = n
for i in range(n + 2, 2*n + 1):
k -= 1
e, o = diff(i)
total += abs(e - o) * k
print total
This could be made even faster if one could find a closed formula for the digit sums, but I think that the absolute function prevents such a solution.