I am trying to solve a problem in software called MyProgrammingLab that is used for teaching in my python class. The question is:
An arithmetic progression is a sequence of numbers in which the distance (or difference) between any two successive numbers if the same. This in the sequence 1, 3, 5, 7, ..., the distance is 2 while in the sequence 6, 12, 18, 24, ..., the distance is 6.
Given the positive integer distance and the positive integer n, associate the variable sum with the sum of the elements of the arithmetic progression from 1 to n with distance "distance". For example, if distance is 2 and n is 10, then sum would be associated with 25 because 1+3+5+7+9 = 25.
My solution was:
sum = 0
for n in range(1,n+1,distance):
sum += n
If I use 10 for example for n, I am getting the right answer 25 but this solution is not being accepted by the software. It saying that the value of n is being modified.
The accepted answer is:
sum = 0
for i in range(1,n+1,distance):
sum += i
Both solutions are giving the same answers if n = 10 but my first solution is giving an error that the value of n was modified.
My professor said that the mean of n, i, sum should be separate which is not in my solution. To demonstrate he said to print out value of n:
sum = 0
n = 10
distance = 2
for n in range(1,n+1,distance):
print(n)
The result is 1,3,5,7 and 9 (original n value of 10 is being changed)
Using the accepted solution:
sum = 0
n = 10
distance = 2
for i in range(1,n+1,distance):
print(n)
Answer is: 10,10,10,10 (the value of n is not change)
So my question is that if both solutions are getting the right answer, what's the significance of value of n changing or not changing ?
The issue here is that you generally want to avoid re-using variable names. There are a ton of caveats -- for example, in many cases its useful to have multiple functions that use the same name for the input arg to imply similarity on what the inputs across the functions should be -- but it's a good rule of thumb.
With that in mind, take a look at your solution:
sum = 0
for n in range(1,n+1,distance):
sum += n
While the number n has been set as 10 (I assume that it's set earlier in the program with a line of n=10? Or maybe it's already in the namespace because of the "MyProgrammingLab" software?), you are then going on to re-use the name n as the variable being incremented in the for-loop. The software is (sorta) correct in saying the value of n is being modified. With every step the for-loop goes, you are assigning a new value to n. The first run of loop, n is 1. The next run of the loop, n is (1+distance), etc. So n is being repeatedly re-assigned.
This causes confusion for what n represents: does it mean how many numbers are being added together? Or is it the current value in this run of the for-loop? To avoid that, it's better to just use an entirely different value for the variable being incremented as the for-loop processes. The official solution uses i, that's fine. You could also say something like current_value, or whatever you find makes it easier to remember what its meaning in that moment is.
Related
The problem is described as follows
https://leetcode.com/problems/target-sum/
You are given a list of non-negative integers, a1, a2, ..., an, and a target, S. Now you have 2 symbols + and -. For each integer, you should choose one from + and - as its new symbol.
Find out how many ways to assign symbols to make sum of integers equal to target S.
Constraints:
The length of the given array is positive and will not exceed 20.
The sum of elements in the given array will not exceed 1000.
Your output answer is guaranteed to be fitted in a 32-bit integer.
I find this submission in leetcode submission detail Accepted Solutions Runtime Distribution
class Solution:
def findTargetSumWays(self, nums, S):
a = sum(nums) - S
if a < 0 or a%2==1: return 0
S = [((1<<(i*21))+1) for i in nums]
return reduce(lambda p,i:(p*i)&(1<<((a//2+1)*21))-1,S,1)>>(21*a//2)
Simplify reduce, it becomes
class Solution:
def findTargetSumWays(self, nums, S):
a = sum(nums) - S
if a < 0 or a%2==1: return 0
auxarr = [((1<<(i*21))+1) for i in nums]
ret=1
for i in auxarr:
ret= (ret*i)&(1<<((a//2+1)*21))-1
return ret>>(21*a//2)
It transforms the original problem into another problem that finds the number of selections that select some nums[i] that their sum is (sum(nums)-S)/2.
I know how to solve such knapsack problems with dp, but I can't understand the above code, I am very curious how such code works, please help me.
# my dp code
class Solution:
def findTargetSumWays(self, nums: List[int], S: int) -> int:
S=sum(nums)-S
if S%2!=0 or S<0: return 0
S//=2
dp=[0]*(S+1)
dp[0]=1
for c in nums:
for j in range(S,c-1,-1):
dp[j]+=dp[j-c]
return dp[S]
It seems to use characteristics of a polynomial where you multiply terms formed of (B^n+1) where B is a power of 2 large enough to avoid overlapping.
So, let's say you have 3 numbers (x,y,z) to add, it will compute:
(B^x + 1)(B^y + 1)(B^z + 1)
The exponents of these polynomials will add up in the result
B^(x+y+z) + B^(x+z) + B^(y+z) + B^z + B^(x+y) + B^x + B^y + 1
So, if any combination of exponent (i.e. numbers) adds up to the same total, the number of times B^total occurs will be the number of ways to obtain that total. Leveraging this characteristic of polynomials, we will find ways*B^total in the result. If the number of ways does not overlap with the value of B^(total+1), it can be extracted using masks and integer divisions.
For example, given 4 numbers h,i,j,k, the products will produce the sum of B raised to powers corresponding to every combination of 1 up to 4 of the numbers added together. So, if we are looking for a total of T, and h+i and j+k equal T. Then the product will contain 2*B^T formed by B^(h+i) + B^(j+k). This corresponds to two ways to form the sum T.
Given that there are 2 possibility for each number (+ or -), there is a maximum of 2^20 possible ways to combine them. To make sure that any sum of B^x does not overlap with B^(x+1), the value of 2^21 is chosen for B.
This is why the offset array (variable name S is a really poor choice here) is formed of (B^n+1) for each n in nums, where B is 2^21, so (2^21^n+1) ... (2^(21n)+1) ... (1<<(21*n))+1
To be able to use the polynomial approach, the problem needs to be converted to an Absence/Presence problem. This is done by reasoning that there has to be a combination of numbers that produces a zero sum by cancelling each other out, leaving the rest to be positive and add up to S. So, if we remove S from the total of numbers, there will be a combination that adds up to half of what remains (a//2). This will be the total we will be looking for.
The reduce function implements the polynomial product and applies a mask ((1<<((a//2+1)*21))-1) to cut off any power of B that is beyond B^(a/2). The final result cuts off the part below B^(a/2) by shifting bits.
This results in the multiple of B^(a/2) which corresponds to the number of ways to produce the sum of exponents (i.e the sum of numbers).
Algorithm problem:
Write a program which takes as input a positive integer n and size
k <= n; return a size-k subset of {0, 1, 2, .. , n -1}. The subset
should be represented as an array. All subsets should be equally
likely, and in addition, all permutations of elements of the array
should be equally likely. You may assume you have a function which
takes as input a nonnegative integer t and returns an integer in the
set {0, 1,...,t-1}.
My original solution to this in pseudocode is as follows:
Set t = n, and output the result of the random number generator into a set() until set() has size(set) == t. Return list(set)
The author solution is as follows:
def online_sampling(n, k):
changed_elements = {}
for i in range(k):
rand_idx = random.randrange(i, n)
rand_idx_mapped = changed_elements.get(rand_idx, rand_idx)
i_mapped = changed_elements.get(i, i)
changed_elements[rand_idx] = i_mapped
changed_elements[i] = rand_idx_mapped
return [changed_elements[i] for i in range(k)]
I totally understand the author's solution - my question is more about why my solution is incorrect. My guess is that it becomes greatly inefficient as t approaches n, because in that case, the probability that I need to keep running the random num function until I get a number that isn't in t gets higher and higher. If t == n, for the very last element to add to set there is just a 1/n chance that I get the correct element, and would probabilistically need to run the given rand() function n times just to get the last item.
Is this the correct reason for why my solution isn't efficient? Is there anything else I'm missing? And how would one describe the time complexity of my solution then? By the above rationale, I believe would be O(n^2) since probabilistically need to run n + n - 1 + n - 2... times.
Your solution is (almost) correct.
Firstly, it will run in O(n log n) instead of O(n^2), assuming that all operations with set are O(1). Here's why.
The expected time to add the first element to the set is 1 = n/n.
The expected time to add the second element to the set is n/(n-1), because the probability to randomly choose yet unchosen element is (n-1)/n. See geometric distribution for an explanation.
...
For k-th element, the expected time is n/(n-k). So for n elements the total time is n/n + n/(n-1) + ... + n/1 = n * (1 + 1/2 + ... + 1/n) = n log n.
Moreover, we can prove by induction that all chosen subsets will be equiprobable.
However, when you do list(set(...)), it is not guaranteed the resulting list will contain elements in the same order as you put them into a set. For example, if set is implemented as a binary search tree then the list will always be sorted. So you have to store the list of unique found elements separately.
UPD (#JimMischel): we proved the average case running time. There still is a possibility that the algorithm will run indefinitely (for example, if rand() always returns 1).
Your method has a big problem. You may return duplicate numbers if you random number generator create same number two times isn't it?
If you say set() will not keep duplicate numbers, your method has created members of set with different chance. So numbers in your set will not be equally likely.
Problem with your method is not efficiency, it does not create an equally likely result set. The author uses a variation of Fisher-Yates method for creating that subset which will be equally likely.
My friend gave me this problem he was asked in an interview that he was not able to answer. After hours of thinking we were not able to come up with the solution.
Consider A number three. I need to write a program to count the different ways in which you can write the number as a sum of numbers less than the number.
For example:
If the number is 2, it can be written as sum(1,1)
if the number is 3, it can be written as sum(1,1,1), sum(1,2), sum(2,1)
if the number is 4, it can be written as sum(1,1,1,1), sum(1,3), sum(3,1), sum(1,2,1), sum(2,1,1), sum(1,1,2), sum(2,2). 7 different ways
If the number is 5, it can be written as sum(1,1,1,1,1), sum(1,1,1,2), sum(1,1,2,1), sum(1,2,1,1), sum(2,1,1,1) etc.
How can I write program to determine the number of ways a number can be broken down into sums of smaller numbers
I was able to come up with a solution for the problem if sum(1,2) and sum(2,1) are considered equivalent using the algorithm in http://www.programminglogic.com/integer-partition-algorithm/
But the problem is sum(1,2) and sum(2,1) are different. I can not see a pattern to do this at all.
Any help would be appreciated. I just want to know the solution.
Think of the number as a line of dots:
4 -> . . . .
Between two adjacent dots we can put a wall to break the number into smaller ones:
1+3 -> .|. . .
2+2 -> . .|. .
1+2+1 -> .|. .|.
For every of the n-1 gaps, we have the option of putting a wall or not, for a total of 2^(n-1) possibilities. However, putting no walls leaves us with just the number, which isn't allowed, so we remove that as a possibility for a final total of 2^(n-1)-1 solutions.
You need a partitioning solution to find all the possible answers. Take a look at the following approaches to do so:
Find all ways to sum given number (with repetitions allowed) from given set
fast method to list all possible combination of numbers which sum to a const number
and a good mathematical explanation:
http://mathworld.wolfram.com/PartitionFunctionP.html
A program for computing the number of different sums (as specified here) could look like this:
def SumsCount(n):
if n <= 1:
return 0
sumsCount = 0
# the first number i can be 1, 2, ..., n-1
for i in range(1,n):
# the rest must sum up to n-i
# or the rest is just the number (n-i)
sumsCount += SumsCount(n-i) + 1
return sumsCount
Here is a simple recursive equation to solve this problem:-
F(N) = (F(N-1)+1) + (F(N-2)+1) + F(N-3)...........F(1)+1
F(2) = 1
F(1) = 0
F(N-1) = (F(N-2)+1) + F(N-3)...........F(1)+1
F(N) = F(N-1) + F(N-1) + 1
F(N) = 2*F(N-1) + 1
Solving equations:
F(N) = 2^(N-2) + 2^(N-2) - 1
= 2^(N-1) - 1
I need help with my code when answering the following question.
An arithmetic progression is a sequence of numbers in which the distance (or difference) between any two successive numbers is the same. This in the sequence 1, 3, 5, 7, ..., the distance is 2 while in the sequence 6, 12, 18, 24, ..., the distance is 6.
Given the positive integer distance and the non-negative integer n, create a list consisting of the arithmetic progression between (and including) 1 and n with a distance of distance. For example, if distance is 2 and n is 8, the list would be [1, 3, 5, 7].
Associate the list with the variable arith_prog.
I updated my progress:
arith_prog = []
for i in range(1, n, distance):
arith_prog.append(n)
total = n + distance
While the suggestions made so far were helpful, I still haven't arrived at the correct solution turingscraft codelab is looking for.
The range function takes up to three arguments; start, stop and step. You want
list(range(1, n, distance))
I'm responding to this as a homework question, since you seem to be indicating that's what it is:
First of all, you never initialize n. What starting value should it
have?
Second, you don't need two loops here - all you need is one.
Third, why are you passing distance to range()? If you pass two
arguments to range() they're treated as a lower and upper bound,
respectively - and distance is probably not a bound.
The problem is where you have arith_prog.append(n). You need to replace the .append(n) with an .append(i) because we are adding the value in that range to list. I just did this homework for 15 minutes ago and that was one of the correct solutions. I made the same error you did.
do something like this
arith_prog = []
n = 5 #this is just for example, you can use anything you like or do an input
distance = 2 #this is also for example, change it to what ever you like
for i in range(1,n,distance):
arith_prog.append(i)
print(arith_prog) #for example this prints out [1,3]
I also encountered this exercise on myprogramminglab. You were very close. Try this:
arith_prog = []
for i in range(1, n + 1, distance):
arith_prog.append(i)
total = n + distance
Hope this helps.
Working through MPL and came across this problem, accepted answer below:
arith_prog=[]
for i in range(1,n+1,distance):
arith_prog.append(i)
I have already solved the problem using mergesort, now I am thinking is that possible to calculate the number using quicksort? I also coded the quicksort, but I don't know how to calculate. Here is my codeļ¼
def Merge_and_Count(AL, AR):
count=0
i = 0
j = 0
A = []
for index in range(0, len(AL) + len(AR)):
if i<len(AL) and j<len(AR):
if AL[i] > AR[j]:
A.append(AR[j])
j = j + 1
count = count+len(AL) - i
else:
A.append(AL[i])
i = i + 1
elif i<len(AL):
A.append(AL[i])
i=i+1
elif j<len(AR):
A.append(AR[j])
j=j+1
return(count,A)
def Sort_and_Count(Arrays):
if len(Arrays)==1:
return (0,Arrays)
list1=Arrays[:len(Arrays) // 2]
list2=Arrays[len(Arrays) // 2:]
(LN,list1) = Sort_and_Count(list1)
(RN,list2) = Sort_and_Count(list2)
(M,Arrays)= Merge_and_Count(list1,list2)
return (LN + RN + M,Arrays)
Generally no, because during the partitioning, when you move a value to its correct side of the pivot, you don't know how many of the values you're moving it past are smaller than it and how many are larger. So, as soon as you do that you've lost information about the number of inversions in the original input.
I come across this problem for some times, As a whole, I think it should be still ok to use quick sort to compute the inversion count, as long as we do some modification to the original quick sort algorithm. (But I have not verified it yet, sorry for that).
Consider an array 3, 6, 2, 5, 4, 1. Support we use 3 as the pivot, the most voted answer is right in that the exchange might mess the orders of the other numbers. However, we might do it different by introducing a new temporary array:
Iterates over the array for the first time. During the iteration, moves all the numbers less than 3 to the temporary array. For each such number, we also records how much number larger than 3 are before it. In this case, the number 2 has one number 6 before it, and the number 1 has 3 number 6, 5, 4 before it. This could be done by a simple counting.
Then we copy 3 into the temporary array.
Then we iterates the array again and move the numbers large than 3 into the temporary array. At last we get 2 1 3 6 5 4.
The problem is that during this process how much inversion pairs are lost? The number is the sum of all the numbers in the first step, and the count of number less than the pivot in the second step. Then we have count all the inversion numbers that one is >= pivot and another is < pivot. Then we could recursively deal with the left part and the right part.