Beautiful sequence - python

A sequence of integers is beautiful if each element of this sequence is divisible by 4.You are given a sequence a1, a2, ..., an. In one step, you may choose any two elements of this sequence, remove them from the sequence and append their sum to the sequence. Compute the minimum number of steps necessary to make the given sequence beautiful else print -1 if this is not possible.
for i in range(int(input())):
n=int(input())
arr=list(map(int,input().split()))
if((sum(arr))%4)!=0:
print(-1)
continue
else:
counter=[]
for i in range(n):
if arr[i]%4!=0:
counter.append(arr[i])
else:
continue
x=sum(counter)
while(x%4==0):
x=x//4
print(x)
My approach:if the sum of the array is not divisible by 4 then the array can not be beautiful else if the sum of the array mod 4 is equal to zero i count the elements in the array whose mod by 4 is not equal to zero and append them in the list and then find the sum of the list and divide the sum by 4 till its quotient modulus 4 is not equal to zero.what i am doing wrong here?
Edit:I have a working script which works well
for i in range(int(input())):
n=int(input())
arr=list(map(int,input().split()))
count1=0
count2=0
count3=0
summ=0
for i in range(n):
x=arr[i]%4
summ+=x
if x==1:
count1+=1
if x==2:
count2+=1
if x==3:
count3+=1
if (summ%4)!=0:
print(-1)
continue
else:
if count2==0 and count1!=0 and count3==0:
tt=count1//4
print(3*tt)
if count2==0 and count1==0 and count3!=0:
tt=count3//4
print(3*tt)
if count2%2==0 and count1==count3:
print(count2//2+count1)
flag1=min(count1,count3)
flag2=abs(count1-count3)
if count2%2==0 and count1!=count3:
flag3=flag2//4
flag4=flag3*3
print(count2//2+ flag1+ flag4)
if count2%2!=0 and count1!=count3:
flag3=flag2-2
flag4=flag3//4
flag5=flag4*3
print(((count2-1)//2)+flag1+flag5+2)

First some observations:
For the sake of 4-divisibility, we can replace all numbers by their division-by-4 remainder, so we only have to cope with values 0, 1, 2 and 3.
The ordering doesn't matter, counting the zeroes, ones, twos and threes is enough.
There are pairs immediately giving a sum divisible by 4: (1, 3) and (2, 2). Each existence of such a pair needs one step.
There are triples (1, 1, 2) and (3, 3, 2) needing two steps.
There are quadruples (1, 1, 1, 1) and (3, 3, 3, 3) needing three steps.
Algorithm:
Count the remainder-0 (can be omitted), remainder-1, remainder-2 and remainder-3 numbers.
If the total sum (from the counts) isn't divisible by 4, there's no solution.
For all the N-tuples described above, find how often they fit into the counts; add the resulting number of steps, subtract the numbers consumed from the counts.
Finally, the remainder-1, remainder-2 and remainder-3 counts should be zero.

Here is an O(N) implementation going pretty much in the direction suggested by Ralf Kleberhoff:
from collections import Counter
def beautify(seq):
# only mod4 is interesting, count 1s, 2s, and 3s
c = Counter(x % 4 for x in seq)
c1, c2, c3 = c.get(1, 0), c.get(2, 0), c.get(3, 0)
steps22, twos = divmod(c2, 2) # you have either 0 or 1 2s left
steps13, ones_or_threes = min(c1, c3), abs(c1 - c3)
if not twos and not ones_or_threes % 4:
# 3 steps for every quadruple of 1s or 3s
return steps22 + steps13 + 3 * ones_or_threes // 4
if twos and ones_or_threes % 2 == 2:
# 2 extra steps to join the remaining 2 1s or 3s with the remaining 2
return steps22 + steps13 + 3 * ones_or_threes // 4 + 2
return -1

I'm not entirely sure what your issue is, but perhaps you could change your approach to the problem. Your logic seems fine, but it seems that your trying to do everything in one go, this problem would be much easier if you break it down into pieces. It looks like it would fit a divide and conquer / recursive approach quite nicely. I also took the liberty of solving this problem myself, as it seems like a fun question to attempt.
Suggestions below
First thing you could do is write a function that finds two numbers that has a sum divisible by k, and return them:
def two_sum(numbers, k):
n = len(numbers)
for i in range(0, n):
for j in range(i+1, n):
if (numbers[i] + numbers[j]) % k == 0:
return numbers[i], numbers[j]
return None
Furthermore, the above function is O(n^2), this could be made more efficient.
Secondly, you could write a recursive function that uses the above function, and has a base case where it stops recursing when all the numbers in the list are divisible by k, therefore the list has become "beautiful". Here is one way of doing this:
def rec_helper(numbers, k, count):
if all(x % k == 0 for x in numbers):
return count
# probably safer to check if two_sum() is not None here
first, second = two_sum(numbers, k)
numbers.remove(first)
numbers.remove(second)
numbers.append(first + second)
return rec_helper(numbers, k, count + 1)
Procedure of above code
Base case: if all the items in the list are currently divisible by k, return the current accumulated count.
Otherwise, obtain a pair of integers whose sum is divisible by k from two_sum()
remove() these two numbers from the list, and append() them to the end of the list.
Finally, call rec_helper() again, with the new modified list and count incremented by one , which is count + 1. count here is the minimum number of steps.
Lastly, you can now write a main calling function:
def beautiful_array(numbers, k):
if sum(numbers) % k != 0:
return -1
return rec_helper(numbers, k, 0)
Which first checks that the sum() of the numbers in the list is divisible by k, before proceeding to calling rec_helper(). If it doesn't pass this test, the function simply returns -1, and the list cannot be made "beautiful".
Behavior of above code
>>> beautiful_array([1, 2, 3, 1, 2, 3, 8], 4)
3
>>> beautiful_array([1, 3, 2, 2, 4, 8], 4)
2
>>> beautiful_array([1, 5, 2, 2, 4, 8], 4)
-1
Note: The above code examples are just suggestions, you can follow or use it however you want to. It also doesn't handle the input(), since I believe the main issue in your code is the approach. I didn't want to create a whole new solution that handles your input as well. Please comment below if their is something wrong with the above code, or if you don't understand anything.

Related

Sum of two squares in Python

I have written a code based on the two pointer algorithm to find the sum of two squares. My problem is that I run into a memory error when running this code for an input n=55555**2 + 66666**2. I am wondering how to correct this memory error.
def sum_of_two_squares(n):
look=tuple(range(n))
i=0
j = len(look)-1
while i < j:
x = (look[i])**2 + (look[j])**2
if x == n:
return (j,i)
elif x < n:
i += 1
else:
j -= 1
return None
n=55555**2 + 66666**2
print(sum_of_two_squares(n))
The problem Im trying to solve using two pointer algorithm is:
return a tuple of two positive integers whose squares add up to n, or return None if the integer n cannot be so expressed as a sum of two squares. The returned tuple must present the larger of its two numbers first. Furthermore, if some integer can be expressed as a sum of two squares in several ways, return the breakdown that maximizes the larger number. For example, the integer 85 allows two such representations 7*7 + 6*6 and 9*9 + 2*2, of which this function must therefore return (9, 2).
You're creating a tuple of size 55555^2 + 66666^2 = 7530713581
So if each element of the tuple takes one byte, the tuple will take up 7.01 GiB.
You'll need to either reduce the size of the tuple, or possibly make each element take up less space by specifying the type of each element: I would suggest looking into Numpy for the latter.
Specifically for this problem:
Why use a tuple at all?
You create the variable look which is just a list of integers:
look=tuple(range(n)) # = (0, 1, 2, ..., n-1)
Then you reference it, but never modify it. So: look[i] == i and look[j] == j.
So you're looking up numbers in a list of numbers. Why look them up? Why not just use i in place of look[i] and remove look altogether?
As others have pointed out, there's no need to use tuples at all.
One reasonably efficient way of solving this problem is to generate a series of integer square values (0, 1, 4, 9, etc...) and test whether or not subtracting these values from n leaves you with a value that is a perfect square.
You can generate a series of perfect squares efficiently by adding successive odd numbers together: 0 (+1) → 1 (+3) → 4 (+5) → 9 (etc.)
There are also various tricks you can use to test whether or not a number is a perfect square (for example, see the answers to this question), but — in Python, at least — it seems that simply testing the value of int(n**0.5) is faster than iterative methods such as a binary search.
def integer_sqrt(n):
# If n is a perfect square, return its (integer) square
# root. Otherwise return -1
r = int(n**0.5)
if r * r == n:
return r
return -1
def sum_of_two_squares(n):
# If n can be expressed as the sum of two squared integers,
# return these integers as a tuple. Otherwise return <None>
# i: iterator variable
# x: value of i**2
# y: value we need to add to x to obtain (i+1)**2
i, x, y = 0, 0, 1
# If i**2 > n / 2, then we can stop searching
max_x = n >> 1
while x <= max_x:
r = integer_sqrt(n-x)
if r >= 0:
return (i, r)
i, x, y = i+1, x+y, y+2
return None
This returns a solution to sum_of_two_squares(55555**2 + 66666**2) in a fraction of a second.
You do not need the ranges at all, and certainly do not need to convert them into tuples. They take a ridiculous amount of space, but you only need their current elements, numbers i and j. Also, as the friendly commenter suggested, you can start with sqrt(n) to improve the performance further.
def sum_of_two_squares(n):
i = 1
j = int(n ** (1/2))
while i < j:
x = i * i + j * j
if x == n:
return j, i
if x < n:
i += 1
else:
j -= 1
Bear in mind that the problem takes a very long time to be solved. Be patient. And no, NumPy won't help. There is nothing here to vectorize.

Big O of backtracking solution counts permutations with range

I have a problem and I've been struggling with my solution time and space complexity:
Given an array of integers (possible duplicates) A and min, low, high are integers.
Find the total number of combinations of items in A that:
low <= A[i] <= high
Each combination has at least min numbers.
Numbers in one combination can be duplicates as they're considered unique in A but combinations can not be duplicates. E.g.: [1,1,2] -> combinations: [1,1],[1,2],[1,1,2] are ok but [1,1],[1,1], [1,2], [2,1] ... are not.
Example: A=[4, 6, 3, 13, 5, 10], min = 2, low = 3, high = 5
There are 4 ways to combine valid integers in A: [4,3],[4,5],[4,3,5],[3,5]
Here's my solution and it works:
class Solution:
def __init__(self):
pass
def get_result(self, arr, min_size, low, high):
return self._count_ways(arr, min_size, low, high, 0, 0)
def _count_ways(self, arr, min_size, low, high, idx, comb_size):
if idx == len(arr):
return 0
count = 0
for i in range(idx, len(arr)):
if arr[i] >= low and arr[i] <= high:
comb_size += 1
if comb_size >= min_size:
count += 1
count += self._count_ways(arr, min_size, low, high, i + 1, comb_size)
comb_size -= 1
return count
I use backtracking so:
Time: O(n!) because for every single integer, I check with each and every single remaining one in worst case - when all integers can form combinations.
Space: O(n) for at most I need n calls on the call stack and I only use 2 variables to keep track of my combinations.
Is my analysis correct?
Also, a bit out of the scope but: Should I do some kind of memoization to improve it?
If I understand your requirements correctly, your algorithm is far too complicated. You can do it as follows:
Compute array B containing all elements in A between low and high.
Return sum of Choose(B.length, k) for k = min .. B.length, where Choose(n,k) is n(n-1)..(n-k+1)/k!.
Time and space complexities are O(n) if you use memoization to compute the numerators/denominators of the Choose function (e.g. if you have already computed 5*4*3, you only need one multiplication to compute 5*4*3*2 etc.).
In your example, you would get B = [4, 3, 5], so B.length = 3, and the result is
Choose(3, 2) + Choose(3, 3)
= (3 * 2)/(2 * 1) + (3 * 2 * 1)/(3 * 2 * 1)
= 3 + 1
= 4
Your analysis of the time complexity isn't quite right.
I understand where you're getting O(n!): the for i in range(idx, len(arr)): loop decreases in length with every recursive call, so it seems like you're doing n*(n-1)*(n-2)*....
However, the recursive calls from a loop of length m do not always contain a loop of size m-1. Suppose your outermost call has 3 elements. The loop iterates through 3 possible values, each spawning a new call. The first such call will have a loop that iterates over 2 values, but the next call iterates over only 1 value, and the last immediately hits your base case and stops. So instead of 3*2*1=((1+1)+(1+1)+(1+1)), you get ((1+0)+1+0).
A call to _count_ways with an array of size n takes twice as long as a call with size n-1. To see this, consider the first branch in the call of size n which is to choose the first element or not. First we choose that first element, which leads to a recursive call with size n-1. Second we do not choose that first element, which gives us n-1 elements left to iterate over, so it's as if we had a second recursive call with size n-1.
Each increase in n increase time complexity by a factor of 2, so the time complexity of your solution is O(2^n). This makes sense: you're checking every combination, and there are 2^n combinations in a set of size n.
However, as you're only trying to count the combinations and not do something with them, this is highly inefficient. See #Mo B.'s answer for a better solution.

Maximum non-contiguous sum of values in list less than or equal to k

I have a list of values [6,1,1,5,2] and a value k = 10. I want to find the maximum sum of values from the list that is less than or equal to k, return the value and the numbers used. In this case the output would be: 10, [6,1,1,2].
I was using this code from GeeksForGeeks as an example but it doesn't work correctly (in this case, the code's result is 9).
The values do not need to be contiguous - they can be in any order.
def maxsum(arr, n, sum):
curr_sum = arr[0]
max_sum = 0
start = 0;
for i in range(1, n):
if (curr_sum <= sum):
max_sum = max(max_sum, curr_sum)
while (curr_sum + arr[i] > sum and start < i):
curr_sum -= arr[start]
start += 1
curr_sum += arr[i]
if (curr_sum <= sum):
max_sum = max(max_sum, curr_sum)
return max_sum
if __name__ == '__main__':
arr = [6, 1, 1, 5, 2]
n = len(arr)
sum = 10
print(maxsum(arr, n, sum))
I also haven't figured out how to output the values that are used for the sum as a list.
This problem is at least as hard as the well-studied subset sum problem, which is NP-complete. In particular, any algorithm which solves your problem can be used to solve the subset sum problem, by finding the maximum sum <= k and then outputting True if the sum equals k, or False if the sum is less than k.
This means your problem is NP-hard, and there is no known algorithm which solves it in polynomial time. Your algorithm's running time is linear in the length of the input array, so it cannot correctly solve the problem, and no similar algorithm can correctly solve the problem.
One approach that can work is a backtracking search - for each element, try including it in the sum, then backtrack and try not including it in the sum. This will take exponential time in the length of the input array.
If your array elements are always integers, another option is dynamic programming; there is a standard dynamic programming algorithm which solves the integer subset sum problem in pseudopolynomial time, which could easily be adapted to solve your form of the problem.
Here's a solution using itertools.combinations. It's fast enough for small lists, but slows down significantly if you have a large sum and large list of values.
from itertools import combinations
def find_combo(values, k):
for num_sum in range(k, 0, -1):
for quant in range(1, len(values) + 1):
for combo in combinations(values, quant):
if sum(combo) == num_sum:
return combo
values = [6, 1, 1, 5, 2]
k = 10
answer = find_combo(values, k)
print(answer, sum(answer))
This solution works for any values in a list and any k, as long as the number of values needed in the solution sum doesn't become large.
The solution presented by user10987432 has a flaw that this function avoids, which is that it always accepts values that keep the sum below k. With that solution, the values are ordered from largest to smallest and then iterated through and added to the solution if it doesn't bring the sum higher than k. However a simple example shows this to be inaccurate:
values = [7, 5, 4, 1] k = 10
In that solution, the sum would begin at 0, then go up to 7 with the first item, and finish at 8 after reaching the last index. The correct solution, however, is 5 + 4 + 1 = 10.

Number of multiples less than the max number

For the following problem on SingPath:
Given an input of a list of numbers and a high number,
return the number of multiples of each of
those numbers that are less than the maximum number.
For this case the list will contain a maximum of 3 numbers
that are all relatively prime to each
other.
Here is my code:
def countMultiples(l, max_num):
counting_list = []
for i in l:
for j in range(1, max_num):
if (i * j < max_num) and (i * j) not in counting_list:
counting_list.append(i * j)
return len(counting_list)
Although my algorithm works okay, it gets stuck when the maximum number is way too big
>>> countMultiples([3],30)
9 #WORKS GOOD
>>> countMultiples([3,5],100)
46 #WORKS GOOD
>>> countMultiples([13,25],100250)
Line 5: TimeLimitError: Program exceeded run time limit.
How to optimize this code?
3 and 5 have some same multiples, like 15.
You should remove those multiples, and you will get the right answer
Also you should check the inclusion exclusion principle https://en.wikipedia.org/wiki/Inclusion-exclusion_principle#Counting_integers
EDIT:
The problem can be solved in constant time. As previously linked, the solution is in the inclusion - exclusion principle.
Let say you want to get the number of multiples of 3 less than 100, you can do this by dividing floor(100/3), the same applies for 5, floor(100/5).
Now to get the multiplies of 3 and 5 that are less than 100, you would have to add them, and subtract the ones that are multiples of both. In this case, subtracting multiplies of 15.
So the answer for multiples of 3 and 5, that are less than 100 is floor(100/3) + floor(100/5) - floor(100/15).
If you have more than 2 numbers, it gets a bit more complicated, but the same approach applies, for more check https://en.wikipedia.org/wiki/Inclusion-exclusion_principle#Counting_integers
EDIT2:
Also the loop variant can be speed up.
Your current algorithm appends multiple in a list, which is very slow.
You should switch the inner and outer for loop. By doing that you would check if any of the divisors divide the number, and you get the the divisor.
So just adding a boolean variable which tells you if any of your divisors divide the number, and counting the times the variable is true.
So it would like this:
def countMultiples(l, max_num):
nums = 0
for j in range(1, max_num):
isMultiple = False
for i in l:
if (j % i == 0):
isMultiple = True
if (isMultiple == True):
nums += 1
return nums
print countMultiples([13,25],100250)
If the length of the list is all you need, you'd be better off with a tally instead of creating another list.
def countMultiples(l, max_num):
count = 0
counting_list = []
for i in l:
for j in range(1, max_num):
if (i * j < max_num) and (i * j) not in counting_list:
count += 1
return count

Generate kth combination without generating/iterating previous

Given a set of items, for example:
[ 1, 2, 3, 4, 5, 6 ]
I'd like to generate all possible combinations of a certain length with repetition. The twist is I'd like to start at a predetermined combination (a sort of offset into the list of combinations).
For example, starting with this:
[ 1, 5, 6 ]
The first (next) combination would be:
[ 1, 6, 6 ]
I've had success using itertools.combinations_with_replacement() to generate the combinations, but the project this is for will require working with a set that generates way too many combinations - creating them all first and iterating to the correct point is not possible.
I've found this example for generating kth combination which doesn't seem to be working very well for me. This answer seemed another possibility, but I can't seem to port it from C to Python.
Here's my code so far using the kth combination example:
import operator as op
items = [ 1,2,3,4,5,6 ]
# https://stackoverflow.com/a/4941932/1167783
def nCr(n, r):
r = min(r, n-r)
if r == 0:
return 1
numer = reduce(op.mul, xrange(n, n-r, -1))
denom = reduce(op.mul, xrange(1, r+1))
return numer // denom
# https://stackoverflow.com/a/1776884/1167783
def kthCombination(k, l, r):
if r == 0:
return []
elif len(l) == r:
return l
else:
i = nCr(len(l)-1, r-1)
if k < i:
return l[0:1] + kthCombination(k, l[1:], r-1)
else:
return kthCombination(k-i, l[1:], r)
# get 1st combination of 3 values from list 'items'
print kthCombination(1, items, 3)
# returns [ 1, 2, 4 ]
Any help would be great!
If you assume that all values in the array are digits in a base-n numbering system where n is the length of the array, the k-th combination will be the equivalent of k expressed in base-n.
If you are wanting to start with a given combination (i.e. [1,6,5]) and continue from there, simply read this starting point as a number in base-n. You can then start iterating through successive combinations by incrementing.
EDIT: Further explanation:
Let's start with the array. The array contains 6 values, so we are working in base-6. We will assume the index of each element in the array is the element's base-6 value.
Values in base-6 range from 0 to 5. This may get confusing because our example uses digits, but we could do this with combinations of anything. I will put 'quote' marks around the digits we are combining.
Given a combination ['1', '6', '5'], we first need to convert this to a base-6 value. '1' becomes 0, '6' becomes 5 and '5' becomes 4. Using their positions in the starting value as their powers in base-6, we get:
(0 * 6^0) + (5 * 6^1) + (4 * 6^2) = 174 (decimal)
If we want to know the next combination, we can add 1. If we want to know 20 combinations ahead, we add 20. We can also subtract to go backwards. Let's add 1 to 174 and convert it back to base-6:
175 (decimal) = (1 + 6^0) + (5 * 6^1) + (4 * 6^2) = 451 (base-6) = ['2', '6', '5'] (combination)
For more on number bases, see http://en.wikipedia.org/wiki/Radix and http://en.wikipedia.org/wiki/Base_%28exponentiation%29
Instead of inventing the wheel time number 37,289,423,987,239,489,826,364,653 (that's counting only human beings), you can map the numbers. itertools will return first combination [1,1,1], but you want [1,5,6]. Simply add [0,4,5] mod 6 to each position. You can also map back and forth between numbers, objects, and modulo, of course.
This works even if the number of elements in each position is different.
You will have more fun with what you started, though.

Categories