Prime factor visitation. Flipping states based on prime factors - python

So I have a list of 1s and 0s:
[1, 1, 0, 0, 1, 1, 0, 1, 1, 1]
and a list of numbers: [3, 4, 15]
I have to find all the prime factors of those numbers, and flip the states in the first list that correspond to the prime factors of those numbers.
So for the above example:
numbers[0] = 3, prime factors are just 3
So after the states were changed, the array look like:
[1, 1, 1, 0, 1, 0, 0, 1, 0, 1], so every (i + 1) % 3 == 0 positions were flipped
numbers[1] = 4, prime factors are just 2
So after the states were changed, the array look like:
[1, 0, 1, 1, 1, 1, 0, 0, 0, 0]
numbers[3] = 15, prime factors are just 3, 5
So after the states were changed, the array look like:
[1, 0, 0, 1, 1, 0, 0, 0, 1, 0]
[1, 0, 0, 1, 0, 0, 0, 0, 1, 1]
Heres what i have so far:
from collections import Counter
def prime_factors(num):
i = 2
factors = []
while i * i < = num:
if (num % i):
i += 1
else:
num //= i
factors.append(i)
if (num > 1):
factors.append(num)
return list(set(factors))
def flip(states, numbers):
factors = []
for num in numbers:
factors.extend(prime_factors(num))
facotrs = Counter(factors)
for key, val in factors.items():
if val % 2:
for i in range(len(states)):
if ((i + 1) % factor == 0):
states[i] = 1 if states[i] == 0 else 0
return states
This works fine, but for large lists, it TLEs.
How do I fix this to make it faster?

for key, val in factors.items():
if val % 2:
for i in range(len(states)):
if ((i + 1) % factor == 0):
states[i] = 1 if states[i] == 0 else 0
In the second for-loop, you don't need to start at 0 in range(len(states)). You can start with factor-1.
In states[i] = 1 if states[i] == 0 else 0,
you can replace this line with XOR operator:
states[i] = states[i]^1.

Try to optimize the prime number generator with the sieve of Eratosthenes. Should work withing a couple seconds for numbers less than 107
import math;
def sieve_primes(number):
a = [True if n>=2 and n%2==1 or n==2 else False for n in range(number+1)];
for x in range(3, (int)(math.sqrt(number))):
if a[x]:
for xx in range(x*x, number, x):
a[xx] = False;
primes = []
for i in range(len(a)):
if a[i]:
primes.append(i);
return primes;
Also, you could avoid multiple calls to the prime generator by finding the biggest number that you'll need to factorize, generating primes for it and storing them for later lookup.

This part:
for i in range(len(states)):
if ((i + 1) % factor == 0):
states[i] = 1 if states[i] == 0 else 0
seems too elaborate. How about (untested!):
for i in range(factor - 1, len(states), factor):
states[i] = 1 - states[i]
? That is, jump directly to all only the indices i such that i+1 is divisible by factor.
Making factoring very fast
Here's suitable sieve code as mentioned in a comment. If the maximum possible number "is large", of course this is an absurd approach. But you haven't told us. If it's up to a few million, this goes fast.
# Return list `sf` such that sf[i] is the smallest prime
# factor of `i`, for 2 <= i <= maxn.
def sieve(maxn):
from math import isqrt
sf = list(range(maxn + 1))
for i in range(4, len(sf), 2):
sf[i] = 2
for p in range(3, isqrt(maxn) + 1, 2):
if sf[p] == p:
for i in range(p * p, len(sf), p + p):
if sf[i] == i:
sf[i] = p
return sf
The whole thing
Here's a whole program. Code for sieve() was already given. Once that's called, it never needs to be called again.
It's often the case that successfully completing timed "programming challenges" crucially relies on the stated input constraints. You were asked several times to tell us what they were, but to no avail. My educated guess is that they put a relatively low limit on the maximum number that needs to be factored. The program here exploits that. But if they didn't put limits on it, there's scant hope for "a fast" solution, because efficient factoring of truly large integers remains a difficult, open research problem.
The method here tries to balance the time and space needed for preprocessing against the time needed to factor. It may or may not be "the best" tradeoff, depending on the still-unknown-to-us input constraints. For example, if the maximum number is "quite" small, you could fully compute - and store - the unique prime factors for every possible number in advance (although a sieve method would remain the fastest easy way to do that).
# Return list of unique prime factors of n.
def upf(n, sf):
result = []
while n > 1:
p = sf[n]
result.append(p)
n //= p
while n % p == 0:
n //= p
return result
MAXN = 1_000_000
sf = sieve(MAXN)
def crunch(nums, bits, sf):
from collections import defaultdict
pcount = defaultdict(int)
for num in nums:
for p in upf(num, sf):
pcount[p] += 1
for p, count in pcount.items():
if count & 1:
for i in range(p - 1, len(bits), p):
bits[i] = 1 - bits[i]
nums = [3, 4, 15]
bits = [1, 1, 0, 0, 1, 1, 0, 1, 1, 1]
expected = [1, 0, 0, 1, 0, 0, 0, 0, 1, 1]
crunch(nums, bits, sf)
assert bits == expected

Related

Finding the shortest subarray that contains k 1's in a binary array

Consider that we have an array of 1's and 0's ( a binary array).
We want to find an algorithm that finds the shortest subarray that contains k 1's.
Just to make it clear, the subarray can contain any amount of 0's but it must contain k 1's.
The algorithm must have time complexiy of O(n).
I tried to write a python code for this problem and this is what I've done so far:
def subarray(arr, k):
sp = 0 # The starting point of the subarray
ep = 0 #The ending point of the subarray
count = 0
result = (0, 0)
n = len(arr)
# Finding a sp
for i in range(0, n):
if arr[i]==1:
sp = i
count+=1
break
#finding a ep
for i in range(sp+1, n):
if arr[i]==1:
count+=1
if count==k:
ep = i
result = (sp, ep)
break
sp+=1
ep+=1
sfound = False
efound = False
while(1):
if ep>n:
break
if arr[sp]==1:
sfound = True
elif(sfound==False and arr[sp]!=1):
sp+=1
if arr[ep]==1:
efound = True
elif(efound==False and arr[ep]!=1):
ep+=1
if (ep-sp)<(result[1]-result[0]):
resut = (sp, ep)
# reseting every thing to start again
sp+=1
ep+=1
sfound = False
efound = False
return result
#main()
A=[0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1,
1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0]
k = 7
print(subarray(A,k))
But I think I wrote it in a complicated way, and it's doesn't give the right answer. I don't khow what's my mistake is?!🤔
Use two pointers, and keep track of the count of ones between them. Advance the appropriate counter to get & maintain k ones. Keep track of the min distance between pointers where you have k ones.

Reducing a N-Sum to a Two Sum

I've recently came across a cool algorithm to reduce any problem of the sort "Find n numbers in an array that sum to a target" to a Two Sum problem. However, I am having a hard time understanding one line of the code.
def findNsum(nums, target, N, result, results):
if len(nums) < N or N < 2 or target < nums[0]*N or target > nums[-1]*N: # early termination
return
if N == 2: # two pointers solve sorted 2-sum problem
l,r = 0,len(nums)-1
while l < r:
s = nums[l] + nums[r]
if s == target:
results.append(result + [nums[l], nums[r]])
l += 1
while l < r and nums[l] == nums[l-1]:
l += 1
elif s < target:
l += 1
else:
r -= 1
else: # recursively reduce N
for i in range(len(nums)-N+1):
if i == 0 or (i > 0 and nums[i-1] != nums[i]):
findNsum(nums[i+1:], target-nums[i], N-1, result+[nums[i]], results)
results = []
findNsum(sorted(nums), 0, 3, [], results)
return results
The condition:
if i == 0 or (i > 0 and nums[i-1] != nums[i]):
Does not make sense to me. Why do I have to check if nums[i-1] != nums[i]? If I try it out with, say, with nums = [-1, 0, 1, 2, 2, -1, -4], I get [[-4, 2, 2], [-1, -1, 2], [-1, 0, 1]] with the condition. If I take it out I get [[-4, 2, 2], [-1, -1, 2], [-1, 0, 1], [-1, 0, 1]]. Can any one make sense of this?
Cheers!
The condition nums[i-1] != nums[i] is to avoid creating duplicate solutions when picking the first element, which can see in your output in the second example. This problem wants to find all unique solutions, not all possible solutions, hence we want to drop the second [-1,0,1]

I don't know what is wrong in my algorithm in code for Google Code Jam 2018 1B-B

Now the contest is over, so I want to ask my algorithmic fails on my code.
This is the problem. If anyone interested, you can see it at here
def solve():
S = int(input())
D, A, B, M, N = [], [], [], [], []
for i in range(S):
d, a, b = [int(c) for c in input().split(" ")]
D.append(d); A.append(a); B.append(b)
M.append(d+a); N.append(d-b)
straightMstart, straightNstart = [0]*(S), [0]*(S)
crossedMstart, crossedNstart = [0]*(S), [0]*(S) # cross over
for i in range(1, S):
if M[i] == M[i-1]:
straightMstart[i] = straightMstart[i-1]
crossedNstart[i] = crossedNstart[i-1]
else:
straightMstart[i] = i
if N[i] == N[i-1]:
straightNstart[i] = straightNstart[i-1]
crossedMstart[i] = crossedMstart[i-1]
else:
straightNstart[i] = i
if M[i] != M[i-1]:
crossedNstart[i] = straightNstart[i-1]
if N[i] != N[i-1]:
crossedMstart[i] = straightMstart[i-1]
maxlen = 1
maxlensubsets = 1
for i in range(1, S):
thislen = i - min(crossedMstart[i], crossedNstart[i]) + 1
if maxlen < thislen:
maxlen = thislen
maxlensubsets = 1
elif maxlen == thislen:
maxlensubsets += 1
# print(crossedNstart)
# print(crossedMstart)
return "%d %d" % (maxlen, maxlensubsets)
testcase = int(input())
for tc in range(1, testcase+1):
print("Case %d: %s" % (tc, solve()))
I used crossed max length to find the maximum size of set.(for M and Ns)
I will give you the following example to make easier to understand my logic:
# Let's suppose that the M and N is:
M (=D[i]+A[i]) = [ 9, 9, 18, 22, 22]
N (=D[i]-B[i]) = [-10, -5, 7, -1, -1]
# Straight Start means starting index for same value.
# M=9 starts from index 0, M=18 starts from index 2, M=22 starts from index 3
straightMstart = [0, 0, 2, 3, 3]
# Same logic applied to straightNstart
straightNstart = [0, 1, 2, 3, 3]
# Crossed Start means cross-starting index of opponent array.
# For crossedMstart, you start from N[i] and climb N then cross to climb M
# The reason why I swapped order of cNs and cMs is that both arrays are based on opponent arrays
crossedNstart = [0, 0, 1, 2, 2]
crossedMstart = [0, 0, 0, 2, 2]
I'm really confusing, I don't really understand what is the point of my fault. Please help me to correct my logic.
Your algorithm has multiple ways it could fail. The simplest of them is entering a string in any of the inputs. Then I do see a problem with straightNstart[i] which is always zero for the current iteration if N[i] or M[i] match N[i-1] or M[i-1]. This sets the crossedMstart[i] and crossedNstart[i] values to 0. Another problem is when S <= 1 which ignores both loops.
The first problem is easy to solve which potentially covers the problems for d, a, b, testcase where d, a, b, testcase should raise only a TypeError:
S = input("[description]")
if S.isdigit():
S = int(S)
if S <= 1:
raise ValueError("Value must be greater then 1")
else:
raise TypeError("Value must be numeric")
For the other problem it could be possible to iterate twice so that the straightNstart[i], straightMstart[i] assignments have any effect on the outcome ( at least not when M[i] or N[i] are not equal to M[i-1] or N[i-1] ).
I found that my algorithm does not handling multiple crossover.

Counting consecutive numbers in all columns of a 2D array

I have a 2d array, X, that looks like this
[0, 0, 0, 2, 1]
[1, 2, 1, 0, 1]
[2, 2, 1, 0, 0]
[0, 0, 1, 2, 0]
I'm trying to iterate through the entire 2D array to try and count all the instances where there are 2 consecutive elements in a column. E.g. X above would return 4 (X[1][1] == X[2][1] && X[1][2] == X[2][2] && X[2][2] == X[3][2] and so on)
I'm finding this very hard to visualize. So far I have:
def get_opposite(number):
if number == 2: return 1
if number == 1: return 2
def counter(X, number):
count = 0
for i in range(len(X)):
for j in range(len(X[i])-1):
if X[i][j] == X[i][j+1] and X[i][j] != 0 and X[i][j] != get_opposite(number):
count += 1
return count
I keep either getting vastly incorrect results, or IndexError, it should be fairly straight forward but I'm not sure what I'm doing wrong
If you compare the example you give in the text with your actual code, you'll notice your code is comparing with the value on the right, not the with the value below it. You need to apply +1 to the first index, not the second. This also means the range of your loops has to be adapted accordingly.
Secondly, you don't need the first function. The equality comparison is enough.
Also, I removed the second argument of the function, as it serves no role:
def counter(X):
count = 0
for i in range(len(X)-1):
for j in range(len(X[i])):
if X[i][j] == X[i+1][j] and X[i][j] != 0:
count += 1
return count

Finding majority votes on -1s, 1s and 0s in list - python

How to find the majority votes for a list that can contain -1s, 1s and 0s?
For example, given a list of:
x = [-1, -1, -1, -1, 0]
The majority is -1 , so the output should return -1
Another example, given a list of:
x = [1, 1, 1, 0, 0, -1]
The majority vote would be 1
And when we have a tie, the majority vote should return 0, e.g.:
x = [1, 1, 1, -1, -1, -1]
This should also return zero:
x = [1, 1, 0, 0, -1, -1]
The simplest case to get the majority vote seem to sum the list up and check whether it's negative, positive or 0.
>>> x = [-1, -1, -1, -1, 0]
>>> sum(x) # So majority -> 0
-4
>>> x = [-1, 1, 1, 1, 0]
>>> sum(x) # So majority -> 1
2
>>> x = [-1, -1, 1, 1, 0]
>>> sum(x) # So majority is tied, i.e. -> 0
0
After the sum, I could do this check to get the majority vote, i.e.:
>>> x = [-1, 1, 1, 1, 0]
>>> majority = -1 if sum(x) < 0 else 1 if sum(x)!=0 else 0
>>> majority
1
>>> x = [-1, -1, 1, 1, 0]
>>> majority = -1 if sum(x) < 0 else 1 if sum(x)!=0 else 0
>>> majority
0
But as noted previously, it's ugly: Python putting an if-elif-else statement on one line and not pythonic.
So the solution seems to be
>>> x = [-1, -1, 1, 1, 0]
>>> if sum(x) == 0:
... majority = 0
... else:
... majority = -1 if sum(x) < 0 else 1
...
>>> majority
0
EDITED
But there are cases that sum() won't work, #RobertB's e.g.
>>> x = [-1, -1, 0, 0, 0, 0]
>>> sum(x)
-2
But in this case the majority vote should be 0!!
I am assuming that votes for 0 count as votes. So sum is not a reasonable option.
Try a Counter:
>>> from collections import Counter
>>> x = Counter([-1,-1,-1, 1,1,1,1,0,0,0,0,0,0,0,0])
>>> x
Counter({0: 8, 1: 4, -1: 3})
>>> x.most_common(1)
[(0, 8)]
>>> x.most_common(1)[0][0]
0
So you could write code like:
from collections import Counter
def find_majority(votes):
vote_count = Counter(votes)
top_two = vote_count.most_common(2)
if len(top_two)>1 and top_two[0][1] == top_two[1][1]:
# It is a tie
return 0
return top_two[0][0]
>>> find_majority([1,1,-1,-1,0]) # It is a tie
0
>>> find_majority([1,1,1,1, -1,-1,-1,0])
1
>>> find_majority([-1,-1,0,0,0]) # Votes for zero win
0
>>> find_majority(['a','a','b',]) # Totally not asked for, but would work
'a'
You could use statistics.mode if you were using python >= 3.4 ,catching a StatisticsError for when you have no unique mode:
from statistics import mode, StatisticsError
def majority(l):
try:
return mode(l)
except StatisticsError:
return 0
The statistics implementation itself uses a Counter dict:
import collections
def _counts(data):
# Generate a table of sorted (value, frequency) pairs.
table = collections.Counter(iter(data)).most_common()
if not table:
return table
# Extract the values with the highest frequency.
maxfreq = table[0][1]
for i in range(1, len(table)):
if table[i][1] != maxfreq:
table = table[:i]
break
return table
def mode(data):
"""Return the most common data point from discrete or nominal data.
``mode`` assumes discrete data, and returns a single value. This is the
standard treatment of the mode as commonly taught in schools:
>>> mode([1, 1, 2, 3, 3, 3, 3, 4])
3
This also works with nominal (non-numeric) data:
>>> mode(["red", "blue", "blue", "red", "green", "red", "red"])
'red'
If there is not exactly one most common value, ``mode`` will raise
StatisticsError.
"""
# Generate a table of sorted (value, frequency) pairs.
table = _counts(data)
if len(table) == 1:
return table[0][0]
elif table:
raise StatisticsError(
'no unique mode; found %d equally common values' % len(table)
)
else:
raise StatisticsError('no mode for empty data')
Another way using a Counter and catching an empty list:
def majority(l):
cn = Counter(l).most_common(2)
return 0 if len(cn) > 1 and cn[0][1] == cn[1][1] else next(iter(cn),[0])[0]
You can count occurences of 0 and test if they are majority.
>>> x = [1, 1, 0, 0, 0]
>>> if sum(x) == 0 or x.count(0) >= len(x) / 2.0:
... majority = 0
... else:
... majority = -1 if (sum(x) < 0) else 1
... majority
0
This solution is based on counting occurrences and sorting:
import operator
def determineMajority(x):
'''
>>> determineMajority([-1, -1, -1, -1, 0])
-1
>>> determineMajority([1, 1, 1, 0, 0, -1])
1
>>> determineMajority([1, 1, 1, -1, -1, -1])
0
>>> determineMajority([1, 1, 1, 0, 0, 0])
0
>>> determineMajority([1, 1, 0, 0, -1, -1])
0
>>> determineMajority([-1, -1, 0, 0, 0, 0])
0
'''
# Count three times
# sort on counts
xs = sorted(
[(i, x.count(i)) for i in range(-1,2)],
key=operator.itemgetter(1),
reverse=True
)
if xs[0][1] > xs[1][1]:
return xs[0][0]
else:
# tie
return 0
if __name__ == '__main__':
import doctest
doctest.testmod()
Additionally, there is one if statements. As mentioned in the comments it is undefined what happens with
x = [1, 1, 0, 0, -1]
# These are your actual votes
votes = [-1, -1, -1, -1, 0]
# These are the options on the ballot
ballot = (-1, 0, 1)
# This is to initialize your counters
counters = {x: 0 for x in ballot}
# Count the number of votes
for vote in votes:
counters[vote] += 1
results = counters.values().sort()
if len(set(values)) < len(ballot) and values[-1] == values [-2]:
# Return 0 if there's a tie
return 0
else:
# Return your winning vote if there isn't a tie
return max(counters, key=counters.get)
A very simple approach.
a = [-1, -1, -1, -1, 0] # Example
count = {}
for i in a:
if i not in count:
count[i] = 1
else:
count[i] += 1
m_count = max(count.values())
for key in count:
if count[key] == m_count:
print key
In the above example the output will be -1,
however if there is a tie, both the keys will be printed.
I believe this works for all provided test cases. Please let me know if I did something wrong.
from collections import Counter
def fn(x):
counts = Counter(x)
num_n1 = counts.get(-1, 0)
num_p1 = counts.get(1, 0)
num_z = counts.get(0, 0)
if num_n1 > num_p1:
return -1 if num_n1 > num_z else 0
elif num_p1 > num_n1:
return 1 if num_p1 > num_z else 0
else:
return 0
from collections import Counter
result = Counter(votes).most_common(2)
result = 0 if result[0][1] == result[1][1] else result[0][0]
Error handling for empty votes lists or votes lists with a set cardinality of 1 is trivial and left as an exercise for the reader.
This works with any number of candidates. If there is a tie between two candidates it returns zero else it returns candidate with most votes.
from collections import Counter
x = [-1, -1, 0, 0, 0, 0]
counts = list((Counter(x).most_common())) ## Array in descending order by votes
if len(counts)>1 and (counts[0][1] == counts[1][1]): ## Comparing top two candidates
print 0
else:
print counts[0][0]
We compare only two candidates because if there is a tie between two candidates it should return 0 and it doesn't depend on third candidate value
An obvious approach is making a counter and updating it according to the data list x. Then you can get the list of numbers (from -1, 0, 1) that are the most frequent. If there is 1 such number, this is what you want, otherwise choose 0 (as you requested).
counter = {-1: 0, 0: 0, 1: 0}
for number in x:
counter[number] += 1
best_values = [i for i in (-1, 0, 1) if counter[i] == max(counter.values())]
if len(best_values) == 1:
majority = best_values[0]
else:
majority = 0
You don't need anything but built-in list operators and stuff, no need to import anything.
votes = [ -1,-1,0,1,0,1,-1,-1] # note that we don't care about ordering
counts = [ votes.count(-1),votes.count(0),votes.count(1)]
if (counts[0]>0 and counts.count(counts[0]) > 1) or (counts[1]>0 and counts.count(counts[1])>1):
majority=0
else:
majority=counts.index(max(counts))-1 # subtract 1 as indexes start with 0
print majority
3d line puts counts of respective votes in a new list, and counts.index() shows us which list position we find the max votes.
I would dare to say that this should be about as pythonic as it can, without getting into eye-gouging oneliners.
Upd: rewrote without text strings and updated to return 0 in case of several equal results (didnt notice this in the original post), added an IF for case if only one vote, eg votes=[-1]
from collections import Counter
def find_majority_vote(votes):
counter = Counter(votes)
most_common = counter.most_common(2)
if len(most_common)==2:
return 0 if most_common[0][1] == most_common[1][1] else most_common[0][0]
else:
return most_common[0][0]
import numpy as np
def fn(vote):
n=vote[np.where(vote<0)].size
p=vote[np.where(vote>0)].size
ret=np.sign(p-n)
z=vote.size-p-n
if z>=max(p,n):
ret=0
return ret
# some test cases
print fn(np.array([-1,-1, 1,1,1,1,0,0,0,0,0,0,0,0]))
print fn(np.array([-1, -1, -1, 1,1,1,0,0]))
print fn(np.array([0,0,0,1,1,1]))
print fn(np.array([1,1,1,1, -1,-1,-1,0]))
print fn(np.array([-1, -1, -1, -1, 1, 0]))

Categories

Resources