Finding numbers that have specific roles - python

I'm trying to find if a number is ordinary or not using inputs. A positive integer is ordinary if it has at least two pairs of positive integer factors such that the difference of one pair equals the sum of the other pair.
For example, 6 is ordinary since 6×1=6,2×3=6,6−1=2+3; and 24 is also ordinary since 12−2=6+4.
I'm stuck on how to make a code where it "grabs" a specific match of the factors of a number.
Thanks :)

First you need a method that will return all factor pairs for a number, this answer provides a decent enough solution https://stackoverflow.com/a/5505024/548562
def f(value):
factors = []
for i in range(1, int(value**0.5)+1):
if value % i == 0:
factors.append((i, value / i))
return factors
Now we have all possible factor pairs we need to loop over every permutation to see if the sum of one pair is equal to the difference of another. itertools provides a permutations function that can be used for this
def is_special(x):
factor_pairs = f(x)
for p1, p2 in itertools.permutations(factor_pairs, 2):
if p1[1] - p1[0] == p2[0] + p2[1]:
return False
return True

Related

How to understand leetcode 494 Target Sum ( knapsack problem ) fastest python code using bit operation

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).

Elements of Programming Interview 5.15 (Random Subset Computation)

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.

Efficiently Predict the Output of a Number Processing Algorithm

I am working on a bit of code that needs to be able to efficiently predict (preferably in O(1) time) the output of the following algorithm when presented with two ints m and n.
algorithm(m,n):
history = set()
while True:
if (m,n) in history:
return False
elif n == m:
return True
else:
history.add((m,n))
if m>n:
x = m-n
y = 2*n
m = x
n = y
else:
x = 2*m
y = n-m
m = x
n = y
Note that when (m,n) appears in the following algorithm's history, you've entered an infinite loop (i.e. 2,1 -> 1,2 -> 2,1...); when m==n the algorithm can proceed only one step further and must terminate (i.e. 5,5 -> 10,0 -> 10,0...). Essentially I need to be able to predict if m(current) and n(current) will ever match.
PS, if this algorithm has a name I'd love to know it. Furthermore, if there exists good reading on this topic (predicting numerical sequences, etc...) I'd love to be directed to it.
Assuming positive integer input, this algorithm will return True if and only if (m+n)/gcd(m, n) is a power of two.
Proof sketch:
Divide both m and n by gcd(m, n) at the start of the algorithm; this will not change the return value.
If the sum of m and n is divisible by an odd prime p after doing this, then both m and n need to become divisible by p for the algorithm to return True, but neither m nor n can do so.
If the sum of m and n is a power of two, then both m and n will become divisible by another factor of 2 on each iteration until both are equal.
First of all, let's reduce the update step to a single line. On each iteration, m updates to the absolute difference; n updates to twice the smaller number.
else:
history.add((m,n))
m, n = abs(m-n), 2 * min(m, n)
This highlights the non-linearity of the iteration. Each update breaks into the two classes you first programmed; the recurrence breaks into multiple classes on each further iteration.
I believe that the short answer for this is no -- you cannot predict the outcome in a time reasonably shorter than simply executing the algorithm.
The division point for switching large vs smaller is when one number is 3 times the other. In that space, the algorithm closes the gap simply: subtract the smaller form the larger, then double the smaller. Once they get within the 3x range, the system quickly turns chaotic: you cannot state that two nearby pairs will have results that remain nearby as the algorithm progresses, not for any adjacent pairs.

How to apply a backtracking algorithm?

I'm doing some excercises in Python course and one of them where I'm stuck is below:
Given a digit sequence that represents a message where each uppercase letter
is replaced with a number (A - 1, B - 2, ... , Z - 26) and space - 0.
Find the number of the initial messages, from which that sequence
could be obtained.
Example: 12345 - 3 (ABCDE, LCDE, AWDE)
11 - 2 (AA, K)
The naive solution is easy and it is simple bruteforce algorithm:
import string
def count_init_messages(sequence):
def get_alpha(seq):
nonlocal count
if len(seq) == 0:
count += 1
return
for i in range(1, len(seq) + 1):
if seq[:i] not in alph_table:
break
else:
get_alpha(seq[i:])
alphabet = " " + string.ascii_uppercase
# generate dictionary of possible digit combination
alph_table = {str(n): alph for n, alph in zip(range(len(alphabet)), alphabet)}
# counter for the right combination met
count = 0
get_alpha(sequence)
return count
def main():
sequence = input().rstrip()
print(count_init_messages2(sequence))
if __name__ == "__main__":
main()
But as the length of an input sequence might be as long as 100 characters and there might be lots of repetition I have met a time limits. For example, one of the sample input is 2222222222222222222222222222222222222222222222222222222222222222222222 (possible messages number is 308061521170129). As my implementation makes too many repetition it takes ages for processing such an input. I think of using the backtracking algorithm, but I haven't realised yet how to implement the memoization for the succesive results.
I'd be glad if it is possible to point me out to the right way how to break that task.
The recurrence relation you have to solve (where s is a string of digits, and a and b are single digits) is this:
S("") = 1
S(a) = 1
S(s + a + b) = S(s+a) + (S(s) if ab is between 10 and 26)
That can be computed using dynamic programming rather than backtracking. If you do it right, it's O(n) time complexity, and O(1) space complexity.
def seq(s):
a1, a2 = 1, 1
for i in xrange(1, len(s)):
a1, a2 = a1 + (a2 if 9 < int(s[i-1:i+1]) < 27 else 0), a1
return a1
print seq('2222222222222222222222222222222222222222222222222222222222222222222222')
The largest number in the lookup table is 26 so you never need to lookup strings of lengths greater than 2. Modify the for loop accordingly. That might be enough to make brute force viable.
You may have also recognized 308061521170129 as the 71st Fibonacci number. This relationship corresponds with the Fibonacci numbers giving "the solution to certain enumerative problems. The most common such problem is that of counting the number of compositions of 1s and 2s that sum to a given total n: there are Fn+1 ways to do this" (https://en.wikipedia.org/wiki/Fibonacci_number#Use_in_mathematics).
Every contiguous subsequence in the string that can be divided into either single or double digit codes represents such an n with multiple possible compositions of 1s and 2s; and thus, for every such subsequence within the string, the result must be multiplied by the (subsequence's length + 1) Fibonacci number (in the case of the 70 2's, we just multiply 1 by the 71st Fibonacci number).

Find values for a given mean

I'm pretty new to python and trying to generate a defined number of numbers (e.g 3 numbers) which mean is equal to a given value.
For example, let's say I'm trying to get different list of 3 numbers whose means equals 10, which would make these lists for example :
(5,10,15) & (0, 0, 30) & (5,5,20).
As I fixed the number of elements in the list I know I could use only the sum but even for that I can't find how to compute different list with the same sum in a pythonic way.
Edit :
I want to generate a defined number of list, not all the possible combination and now that I think about it, it should be only integers
Here you go.
This is only for positive integers with no duplicates
def make_lists(mean):
'without duplicates'
for i in range(0, mean+1):
for j in range(i, mean+1):
k = mean * 3 - i - j
assert (k+i+j) / 3.0 == float(mean), ((k+i+j) / 3.0) #just testing
yield (i,j, k)
if __name__ == '__main__':
print( list(make_lists(10)))

Categories

Resources