python geometric sequence - python

I'm trying to do a problem in my book but I have no idea how. The question is, Write function geometric() that takes a list of integers as input and returns True if the integers in the list form a geometric sequence. A sequence a0,a1,a2,a3,a4,...,an-2,an-1 is a geometric sequence if the ratios a1/a0,a2/a1,a3/a2,a4/a3,...,an-1/an-2 are all equal.
def geometric(l):
for i in l:
if i*1==i*0:
return True
else:
return False
I honestly have no idea how to start this and I'm completely drawing a blank. Any help would be appreciated.
Thanks!
For example:
geometric([2,4,8,16,32,64,128,256])
>>> True
geometric([2,4,6,8])`
>>> False

This should efficiently handle all iterable objects.
from itertools import izip, islice, tee
def geometric(obj):
obj1, obj2 = tee(obj)
it1, it2 = tee(float(x) / y for x, y in izip(obj1, islice(obj2, 1, None)))
return all(x == y for x, y in izip(it1, islice(it2, 1, None)))
assert geometric([2,4,8,16,32,64,128,256])
assert not geometric([2,4,6,8])
Check out itertools - http://docs.python.org/2/library/itertools.html

One easy method would be like this:
def is_geometric(a):
r = a[1]/float(a[0])
return all(a[i]/float(a[i-1]) == r for i in xrange(2,len(a)))
Basically, it calculates the ratio between the first two, and uses all to determine if all members of the generator are true. Each member of the generator is a boolean value representing whether the ratio between two numbers is equal to the ratio between the first two numbers.

Here's my solution. It's essentially the same as pyrospade's itertools code, but with the generators disassembled. As a bonus, I can stick to purely integer math by avoid doing any division (which might, in theory, lead to floating point rounding issues):
def geometric(iterable):
it = iter(iterable)
try:
a = next(it)
b = next(it)
if a == 0 or b == 0:
return False
c = next(it)
while True:
if a*c != b*b: # <=> a/b != b/c, but uses only int values
return False
a, b, c = b, c, next(it)
except StopIteration:
return True
Some test results:
>>> geometric([2,4,8,16,32])
True
>>> geometric([2,4,6,8,10])
False
>>> geometric([3,6,12,24])
True
>>> geometric(range(1, 1000000000)) # only iterates up to 3 before exiting
False
>>> geometric(1000**n for n in range(1000)) # very big numbers are supported
True
>>> geometric([0,0,0]) # this one will probably break every other code
False

Like this
def is_geometric(l):
if len(l) <= 1: # Edge case for small lists
return True
ratio = l[1]/float(l[0]) # Calculate ratio
for i in range(1, len(l)): # Check that all remaining have the same ratio
if l[i]/float(l[i-1]) != ratio: # Return False if not
return False
return True # Return True if all did
And for the more adventurous
def is_geometric(l):
if len(l) <= 1:
return True
r = l[1]/float(l[0])
# Check if all the following ratios for each
# element divided the previous are equal to r
# Note that i is 0 to n-1 while e is l[1] to l[n-1]
return all(True if e/float(l[i]) == r else False for (i, e) in enumerate(l[1:]))

Related

How to solve the duplicate integer issue in an array for the isValidSubsequence(array, sequence) question

prompt for isValidSubsequence
def isValidSubsequence(array, sequence):
index = -1
# issue is with initialising the index variable
if len(sequence)<=len(array):
for i in range(len(sequence)):
# i refers to the i in the sequence
if sequence[i] in array:
# causing a problem for repetitions such as array = [1,1,1,1,1] and subsequence = [1,1,1]
# array.index(sequence[i]) would call the first 1 instead of the second 1
if array.index(sequence[i]) > index:
index = array.index(sequence[i])
else:
return False
else:
return False
return True
else:
return False
How to solve this repeating 1s issue using my code?
The reason array.index(sequence[i]) calls the first 1 is because sequence[i] is 1 in your case, and that is the same as if you called array.index(1). The index function searches your array for 1 and as soon as it finds it, it returns its index, so it always finds the first 1 first and then stops looking and returns its index.
Try something like this, this should work:
def isValidSubsequence(array, sequence):
lastIndex = -1
if not len(array) >= len(sequence):
return False
for i in range(len(sequence)):
containsInt = False
for j in range(lastIndex + 1, len(array)):
if array[j] == sequence[i]:
containsInt = True
lastIndex = j
break
if not containsInt:
return False
return True
You are using lists, not arrays. list.index(x[, start[, end]]) can take positionals from where to where search - you could rewrite your algo to remember the last found position for each value and look for new values from that position.
BUT: this is irrelevant, you only need to concern yourself with how many of each element are in original and sequence and if one has at most equal of them...
The correct way to solve this ist to count the occurences of elements in the original, subtract the amount of occurences in the `sequence`` and evaluate your numbers:
If sequence is empty, contains elements not in original or contains more elements then inside original it can not be a subsequence:
from collections import Counter
def isValidSubsequence(array, sequence):
# check empty sequence
if not sequence:
return False
original = Counter(array)
# check any in seq that is not in original
if any(i not in original for i in sequence):
return False
# remove number of occurences in sequence from originals
original.subtract(sequence)
# check if too many of them in sequence
if any(v < 0 for v in original.values()):
return False
return True
Testcode:
source = [1,2,3,4]
test_cases = [[1], [2,4], [1,2,4] ]
neg_test_case = [[2,5], [9], []]
for tc in test_cases:
print(isValidSubsequence(source,tc), "should be True")
for ntc in neg_test_case:
print(isValidSubsequence(source,ntc), "should be False")
Output:
True should be True
True should be True
True should be True
False should be False
False should be False
False should be False
If you are not allowed to import, count yourself (expand this yourself):
# not efficient
counted = {d:original.count(d) for d in frozenset(original)}
# remove numbers
for d in sequence:
counted.setdefault(d,0)
counted[d] -= 1
This would be another way to answer the question.
def isValidSubsequence(array,sequence):
if len(array) >= len(sequence):
iter_sequence = iter(sequence)
last = next(iter_sequence)
total = 0
for i in array:
if i + total == last:
total += i
try:
last += next(iter_sequence)
except StopIteration:
return True
break
return False

Boolean True or False upon finding two consecutive digits in a given integer without using any built-in functions

To find two consecutive digits(D) in a given integer(N) without using any built-in functions and returning True or False, the following code seems to be exiting when coming across one D, however it works if there are two Ds. Why is it not working as it is and how to fix it? Thanks!
def double_digits(n, d):
"""Return True if N has two Ds in a row otherwise return False.
int, int -> Boolean
>>> double_digits(91019, 1)
False
>>> double_digits(88, 8)
True
>>> double_digits(2772, 7)
True
>>> double_digits(88108, 0)
False
>>> double_digits(12345, 4)
False
>>> double_digits(81811081, 1)
True
"""
while n > 0:
remainder = n % 10
n = n // 10
if remainder == d:
if n % 10 == d:
return True
else:
remainder, n = n % 10, n // 10
return False
The last return statement should be out of the loop.
Below is the correct code:
def double_digits(n, d):
while n > 0:
remainder = n % 10
n = n // 10
if remainder == d:
if n % 10 == d:
return True
else:
remainder, n = n % 10, n // 10
return False
You must de-indent the last statement : return False, 4 spaces to the left. That must help you.
Alternatively you can convert the input to a string and then the character matching would be easy.
def double_digits(n: int, d: int)->bool:
n = str(n)
d = str(d)
i = 0
while i < len(n)-1:
if n[i] == d and n[i+1] == d:
return True
i+=1
return False
Here I have used some builtin fuctions like len and str, but if you explicitly
want to avoid using them, just go with your original approach and just de-indent the return statement once

How to see if the list contains consecutive numbers

I want to test if a list contains consecutive integers and no repetition of numbers.
For example, if I have
l = [1, 3, 5, 2, 4, 6]
It should return True.
How should I check if the list contains up to n consecutive numbers without modifying the original list?
I thought about copying the list and removing each number that appears in the original list and if the list is empty then it will return True.
Is there a better way to do this?
For the whole list, it should just be as simple as
sorted(l) == list(range(min(l), max(l)+1))
This preserves the original list, but making a copy (and then sorting) may be expensive if your list is particularly long.
Note that in Python 2 you could simply use the below because range returned a list object. In 3.x and higher the function has been changed to return a range object, so an explicit conversion to list is needed before comparing to sorted(l)
sorted(l) == range(min(l), max(l)+1))
To check if n entries are consecutive and non-repeating, it gets a little more complicated:
def check(n, l):
subs = [l[i:i+n] for i in range(len(l)) if len(l[i:i+n]) == n]
return any([(sorted(sub) in range(min(l), max(l)+1)) for sub in subs])
The first code removes duplicates but keeps order:
from itertools import groupby, count
l = [1,2,4,5,2,1,5,6,5,3,5,5]
def remove_duplicates(values):
output = []
seen = set()
for value in values:
if value not in seen:
output.append(value)
seen.add(value)
return output
l = remove_duplicates(l) # output = [1, 2, 4, 5, 6, 3]
The next set is to identify which ones are in order, taken from here:
def as_range(iterable):
l = list(iterable)
if len(l) > 1:
return '{0}-{1}'.format(l[0], l[-1])
else:
return '{0}'.format(l[0])
l = ','.join(as_range(g) for _, g in groupby(l, key=lambda n, c=count(): n-next(c)))
l outputs as: 1-2,4-6,3
You can customize the functions depending on your output.
We can use known mathematics formula for checking consecutiveness,
Assuming min number always start from 1
sum of consecutive n numbers 1...n = n * (n+1) /2
def check_is_consecutive(l):
maximum = max(l)
if sum(l) == maximum * (maximum+1) /2 :
return True
return False
Once you verify that the list has no duplicates, just compute the sum of the integers between min(l) and max(l):
def check(l):
total = 0
minimum = float('+inf')
maximum = float('-inf')
seen = set()
for n in l:
if n in seen:
return False
seen.add(n)
if n < minimum:
minimum = n
if n > maximum:
maximum = n
total += n
if 2 * total != maximum * (maximum + 1) - minimum * (minimum - 1):
return False
return True
import numpy as np
import pandas as pd
(sum(np.diff(sorted(l)) == 1) >= n) & (all(pd.Series(l).value_counts() == 1))
We test both conditions, first by finding the iterative difference of the sorted list np.diff(sorted(l)) we can test if there are n consecutive integers. Lastly, we test if the value_counts() are all 1, indicating no repeats.
I split your query into two parts part A "list contains up to n consecutive numbers" this is the first line if len(l) != len(set(l)):
And part b, splits the list into possible shorter lists and checks if they are consecutive.
def example (l, n):
if len(l) != len(set(l)): # part a
return False
for i in range(0, len(l)-n+1): # part b
if l[i:i+3] == sorted(l[i:i+3]):
return True
return False
l = [1, 3, 5, 2, 4, 6]
print example(l, 3)
def solution(A):
counter = [0]*len(A)
limit = len(A)
for element in A:
if not 1 <= element <= limit:
return False
else:
if counter[element-1] != 0:
return False
else:
counter[element-1] = 1
return True
The input to this function is your list.This function returns False if the numbers are repeated.
The below code works even if the list does not start with 1.
def check_is_consecutive(l):
"""
sorts the list and
checks if the elements in the list are consecutive
This function does not handle any exceptions.
returns true if the list contains consecutive numbers, else False
"""
l = list(filter(None,l))
l = sorted(l)
if len(l) > 1:
maximum = l[-1]
minimum = l[0] - 1
if minimum == 0:
if sum(l) == (maximum * (maximum+1) /2):
return True
else:
return False
else:
if sum(l) == (maximum * (maximum+1) /2) - (minimum * (minimum+1) /2) :
return True
else:
return False
else:
return True
1.
l.sort()
2.
for i in range(0,len(l)-1)))
print(all((l[i+1]-l[i]==1)
list must be sorted!
lst = [9,10,11,12,13,14,15,16]
final = True if len( [ True for x in lst[:-1] for y in lst[1:] if x + 1 == y ] ) == len(lst[1:]) else False
i don't know how efficient this is but it should do the trick.
With sorting
In Python 3, I use this simple solution:
def check(lst):
lst = sorted(lst)
if lst:
return lst == list(range(lst[0], lst[-1] + 1))
else:
return True
Note that, after sorting the list, its minimum and maximum come for free as the first (lst[0]) and the last (lst[-1]) elements.
I'm returning True in case the argument is empty, but this decision is arbitrary. Choose whatever fits best your use case.
In this solution, we first sort the argument and then compare it with another list that we know that is consecutive and has no repetitions.
Without sorting
In one of the answers, the OP commented asking if it would be possible to do the same without sorting the list. This is interesting, and this is my solution:
def check(lst):
if lst:
r = range(min(lst), max(lst) + 1) # *r* is our reference
return (
len(lst) == len(r)
and all(map(lst.__contains__, r))
# alternative: all(x in lst for x in r)
# test if every element of the reference *r* is in *lst*
)
else:
return True
In this solution, we build a reference range r that is a consecutive (and thus non-repeating) sequence of ints. With this, our test is simple: first we check that lst has the correct number of elements (not more, which would indicate repetitions, nor less, which indicates gaps) by comparing it with the reference. Then we check that every element in our reference is also in lst (this is what all(map(lst.__contains__, r)) is doing: it iterates over r and tests if all of its elements are in lts).
l = [1, 3, 5, 2, 4, 6]
from itertools import chain
def check_if_consecutive_and_no_duplicates(my_list=None):
return all(
list(
chain.from_iterable(
[
[a + 1 in sorted(my_list) for a in sorted(my_list)[:-1]],
[sorted(my_list)[-2] + 1 in my_list],
[len(my_list) == len(set(my_list))],
]
)
)
)
Add 1 to any number in the list except for the last number(6) and check if the result is in the list. For the last number (6) which is the greatest one, pick the number before it(5) and add 1 and check if the result(6) is in the list.
Here is a really short easy solution without having to use any imports:
range = range(10)
L = [1,3,5,2,4,6]
L = sorted(L, key = lambda L:L)
range[(L[0]):(len(L)+L[0])] == L
>>True
This works for numerical lists of any length and detects duplicates.
Basically, you are creating a range your list could potentially be in, editing that range to match your list's criteria (length, starting value) and making a snapshot comparison. I came up with this for a card game I am coding where I need to detect straights/runs in a hand and it seems to work pretty well.

Find permutation list

I have a task. I know this task is really easy but..
A non-empty zero-indexed array A consisting of N integers is given.
A permutation is a sequence containing each element from 1 to N once, and only once.
For example, array A such that:
A[0] = 4
A[1] = 1
A[2] = 3
A[3] = 2
is a permutation, but array A such that:
A[0] = 4
A[1] = 1
A[2] = 3
is not a permutation.
The goal is to check whether array A is a permutation.
I implemented this solution, but I think this isn't the best solution.
def solution(A):
# write your code in Python 2.6
maxN = max(A)
B = list(xrange(1,maxN+1))
if sorted(A) == sorted(B):
return 1
else:
return 0
Do you have any ideas how I should solve this problem?
def solution(A):
N = len(A)
return min(A) == 1 and max(A) == N and len(set(A)) == N
That takes (expected) time linear in N, so is expected to be faster than sorting. But it does rely on the stated assumption that all list entries are in fact integers. If they're not, then, for example,
>>> solution([1, 2, 4, 3.14159])
True
If both arrays should have all numbers from 1 to N you shouldn't sort them, all you need to do is make sure they actually have all the numbers.
so this is your algorithm (working solution soon):
make sure the list is of size N, otherwise return false.
generate list of N zeros (We will refer to it as zero-list)
for each member (say it's value is m) of the list (both) increase the zero-list[m-1] by 1
make sure all members of zero-list is 1
This is O(n) as opposed to O(nlogn) that you have right now.
What you are doing verifies that each member from 1 to N exists in the list you're given
#!/usr/bin/env python
def is_perm(l, n):
# make sure initial criterias are met
if len(l) != max(l) or len(l) != n:
return False
# make room for a list that verifies existence from 1 to N
zero_list = [0 for i in l]
# "mark" using the new list each member of the input list
for i in l:
try:
zero_list[i-1] += 1
except:
return False
# make sure all members have been "marked"
for i in zero_list:
if zero_list[i] != 1:
return False
# if all is good, the earth is saved
return True
if "__main__" == __name__:
print is_perm([1, 2, 3, 3], 4) # false
print is_perm([1, 2, 4, 3], 4) # true
print is_perm([1, 2, 4, 300000000000000], 4) # false
def is_perm(lst):
n = len(lst)
if min(lst) != 1 or max(lst) != n:
return False
cnt = [1] * n
for e in lst:
cnt[e - 1] -= 1
return not any(cnt)
It has a worst case complexity O(n) which is of course optimal.
Just check if all elements are present ...
In [36]: all(map(lambda p: p in [1,4,3,2], range(1,5)))
Out[36]: True
In [37]: all(map(lambda p: p in [1,4,3,2444], range(1,5)))
Out[37]: False
In [38]: all(map(lambda p: p in [1,4,3], range(1,5)))
Out[38]: False
So I found solution which works great:
def solution(A):
A.sort(reverse=True)
return 1 if A[0] == len(A) and len(set(A)) == len(A) else 0
What about this?:
>>> def is_permutation(a, n):
if len(a) != n: return False
h = {}
for i in a:
if not 1 <= i <= n: return False
if h.setdefault(i, False): return False
h[i] = True
return True
>>> is_permutation([1,4,2,3], 4)
True
>>> is_permutation([1,4,2,0], 4)
False
>>> is_permutation([1,4,2],4)
False
Here is fastest way:
def solution(A):
'''
>>> solution([4,1,3,2])
1
>>> solution([2,3])
0
'''
suma = sum(A)
N = len(A)
if (N*(N+1)/2)!=suma:
return 0
return 1

Subset sum recursively in Python

I will be happy to get some help.
I have the following problem:
I'm given a list of numbers seq and a target number and I need to write 2 things:
A recursive solution that returns True if there is a sum of a subsequence that equals the target number and False otherwise.
example:
subset_sum([-1,1,5,4],0) # True
subset_sum([-1,1,5,4],-3) # False
Secondly, I need to write a solution using what I wrote in the previous solution
but now with memoization that uses a dictionary in which the keys are tuples:
(len(seq),target)
For number 1 this is what I got to so far:
def subset_sum(seq, target):
if target == 0:
return True
if seq[0] == target:
return True
if len(seq) > 1:
return subset_sum(seq[1:],target-seq[0]) or subset_sum(seq[1:],target)
return False
Not sure I got it right so if I could get some input I will be grateful.
For number 2:
def subset_sum_mem(seq, target, mem=None ):
if not mem:
mem = {}
key=(len(seq),target)
if key not in mem:
if target == 0 or seq[0]==target:
mem[key] = True
if len(seq)>1:
mem[key] = subset_sum_mem(seq[1:],target-seq[0],mem) or subset_sum_mem(seq[1:],target,mem)
mem[key] = False
return mem[key]
I can't get the memoization to give me the correct answer so I'd be glad for some guidance here.
Thanks for anyone willing to help!
Just for reference, here's a solution using dynamic programming:
def positive_negative_sums(seq):
P, N = 0, 0
for e in seq:
if e >= 0:
P += e
else:
N += e
return P, N
def subset_sum(seq, s=0):
P, N = positive_negative_sums(seq)
if not seq or s < N or s > P:
return False
n, m = len(seq), P - N + 1
table = [[False] * m for x in xrange(n)]
table[0][seq[0]] = True
for i in xrange(1, n):
for j in xrange(N, P+1):
table[i][j] = seq[i] == j or table[i-1][j] or table[i-1][j-seq[i]]
return table[n-1][s]
This is how I'd write the subset_sum:
def subset_sum(seq, target):
if target == 0:
return True
for i in range(len(seq)):
if subset_sum(seq[:i] + seq[i+1:], target - seq[i]):
return True
return False
It worked on a couple of examples:
>>> subset_sum([-1,1,5,4], 0))
True
>>> subset_sum([-1,1,5,4], 10)
True
>>> subset_sum([-1,1,5,4], 4)
True
>>> subset_sum([-1,1,5,4], -3)
False
>>> subset_sum([-1,1,5,4], -4)
False
To be honest I wouldn't know how to memoize it.
Old Edit: I removed the solution with any() because after some tests I found out that to be slower!
Update: Just out of curiosity you could also use itertools.combinations:
from itertools import combinations
def com_subset_sum(seq, target):
if target == 0 or target in seq:
return True
for r in range(2, len(seq)):
for subset in combinations(seq, r):
if sum(subset) == target:
return True
return False
This can do better that the dynamic programming approach in some cases but in others it will hang (it's anyway better then the recursive approach).
I have this modified code:
def subset_sum(seq, target):
left, right = seq[0], seq[1:]
return target in (0, left) or \
(bool(right) and (subset_sum(right, target - left) or subset_sum(right, target)))
def subset_sum_mem(seq, target, mem=None):
mem = mem or {}
key = (len(seq), target)
if key not in mem:
left, right = seq[0], seq[1:]
mem[key] = target in (0, left) or \
(bool(right) and (subset_sum_mem(right, target - left, mem) or subset_sum_mem(right, target, mem)))
return mem[key]
Can you provide some test cases this does not work for?

Categories