Related
I am trying to make a function that counts the number of cycles within a permutated list.
I do sometimes get the right answer when running the code, but most times I receive an error message - and I am unable to figure out why.
My code is as follows:
def count_cycles(n):
cycle_count = 0
copy_list = []
for element in n:
copy_list.append(element)
while len(copy_list) != 0:
ran_num = random.choice(copy_list)
while True:
if n[ran_num] == ran_num:
cycle_count = circle_count + 1
if int(ran_num) in copy_list:
copy_list.remove(ran_num)
break
else:
n.insert(ran_num, ran_num)
print(n, ran_num, copy_list)
ran_num = n[ran_num + 1]
print(ran_num)
copy_list.remove(ran_num)
n.remove(ran_num)
continue
return print(cycle_count, n)
What I use is that I test with this permutated list with 3 cycles [2, 6, 0, 3, 1, 4, 5].
Picture of output from a correct and incorrect run
I used print(n, ran_num, copy_list) to assess the output as per the picture.
Here is one possibility:
p = [2, 6, 0, 3, 1, 4, 5]
cycles = set()
elts = set(range(len(p)))
while elts:
cycle = []
x0 = elts.pop()
cycle.append(x0)
x = p[x0]
while x != x0:
cycle.append(x)
x = p[x]
elts -= set(cycle)
cycles.add(tuple(cycle))
print(cycles)
It gives:
{(0, 2), (1, 6, 5, 4), (3,)}
Then to get the number of cycles you can use len(cycles).
In addition to the existing answer, sympy provides some functionality to work with permutations. In this case, you could use the following:
from sympy.combinatorics import Permutation
p = Permutation([2, 6, 0, 3, 1, 4, 5])
num_cycles = p.cycles # 3
Hey this is my first question so I hope I'm doing it right.
I'm trying to write a function that given a list of integers and N as the maximum occurrence, would then return a list with any integer above the maximum occurrence deleted. For example if I input:
[20,37,20,21] #list of integers and 1 #maximum occurrence.
Then as output I would get:
[20,37,21] because the number 20 appears twice and the maximum occurrence is 1, so it is deleted from the list. Here's another example:
Input: [1,1,3,3,7,2,2,2,2], 3
Output: [1,1,3,3,7,2,2,2]
Here's what I wrote so far, how would I be able to optimize it? I keep on getting a timeout error. Thank you very much in advance.
def delete_nth(order,n):
order = Counter(order)
for i in order:
if order[i] > n:
while order[i] > n:
order[i] - 1
return order
print(delete_nth([20,37,20,21], 1))
You can remove building the Counter at the beginning - and just have temporary dictionary as counter:
def delete_nth(order,n):
out, counter = [], {}
for v in order:
counter.setdefault(v, 0)
if counter[v] < n:
out.append(v)
counter[v] += 1
return out
print(delete_nth([20,37,20,21], 1))
Prints:
[20, 37, 21]
You wrote:
while order[i] > n:
order[i] - 1
That second line should presumably be order[i] -= 1, or any code that enters the loop will never leave it.
You could use a predicate with a default argument collections.defaultdict to retain state as your list of numbers is being filtered.
def delete_nth(numbers, n):
from collections import defaultdict
def predicate(number, seen=defaultdict(int)):
seen[number] += 1
return seen[number] <= n
return list(filter(predicate, numbers))
print(delete_nth([1, 1, 3, 3, 7, 2, 2, 2, 2], 3))
Output:
[1, 1, 3, 3, 7, 2, 2, 2]
>>>
I've renamed variables to something that had more meaning for me:
This version, though very short and fairly efficient, will output identical values adjacently:
from collections import Counter
def delete_nth(order, n):
counters = Counter(order)
output = []
for value in counters:
cnt = min(counters[value], n)
output.extend([value] * cnt)
return output
print(delete_nth([1,1,2,3,3,2,7,2,2,2,2], 3))
print(delete_nth([20,37,20,21], 1))
Prints:
[1, 1, 2, 2, 2, 3, 3, 7]
[20, 37, 21]
This version will maintain original order, but run a bit more slowly:
from collections import Counter
def delete_nth(order, n):
counters = Counter(order)
for value in counters:
counters[value] = min(counters[value], n)
output = []
for value in order:
if counters[value]:
output.append(value)
counters[value] -= 1
return output
print(delete_nth([1,1,2,3,3,2,7,2,2,2,2], 3))
print(delete_nth([20,37,20,21], 1))
Prints:
[1, 1, 2, 3, 3, 2, 7, 2]
[20, 37, 21]
Here is what I am trying to do. Given a number and a set of numbers, I want to partition that number into the numbers given in the set (with repetitions).
For example :
take the number 9, and the set of numbers = {1, 4, 9}.
It will yield the following partitions :
{ (1, 1, 1, 1, 1, 1, 1, 1, 1), (1, 1, 1, 1, 1, 4), (1, 4, 4), (9,)}
No other possible partitions using the set {1, 4, 9} cannot be formed to sum the number 9.
I wrote a function in Python which do the task :
S = [ 1, 4, 9, 16 ]
def partition_nr_into_given_set_of_nrs(nr , S):
lst = set()
# Build the base case :
M = [1]*(nr%S[0]) + [S[0]] * (nr //S[0])
if set(M).difference(S) == 0 :
lst.add(M)
else :
for x in S :
for j in range(1, len(M)+1):
for k in range(1, nr//x +1 ) :
if k*x == sum(M[:j]) :
lst.add( tuple(sorted([x]*k + M[j:])) )
return lst
It works correctly but I want to see some opinions about it. I'm not satisfied about the fact that it uses 3 loops and I guess that it can be improved in a more elegant way. Maybe recursion is more suited in this case. Any suggestions or corrections would be appreciated. Thanks in advance.
I would solve this using a recursive function, starting with the largest number and recursively finding solutions for the remaining value, using smaller and smaller numbers.
def partition_nr_into_given_set_of_nrs(nr, S):
nrs = sorted(S, reverse=True)
def inner(n, i):
if n == 0:
yield []
for k in range(i, len(nrs)):
if nrs[k] <= n:
for rest in inner(n - nrs[k], k):
yield [nrs[k]] + rest
return list(inner(nr, 0))
S = [ 1, 4, 9, 16 ]
print(partition_nr_into_given_set_of_nrs(9, S))
# [[9], [4, 4, 1], [4, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1]]
Of course you could also do without the inner function by changing the parameters of the function and assuming that the list is already sorted in reverse order.
If you want to limit the number of parts for large numbers, you can add an aditional parameter indicating the remaining allowed number of elements and only yield result if that number is still greater than zero.
def partition_nr_into_given_set_of_nrs(nr, S, m=10):
nrs = sorted(S, reverse=True)
def inner(n, i, m):
if m > 0:
if n == 0:
yield []
for k in range(i, len(nrs)):
if nrs[k] <= n:
for rest in inner(n - nrs[k], k, m - 1):
yield [nrs[k]] + rest
return list(inner(nr, 0, m))
Here is a solution using itertools and has two for loops so time complexity is about O(n*n) (roughly)
A little memoization applied to reshape list by removing any element that is greater than max sum needed.
Assuming you are taking sum to be max of your set (9 in this case).
sourceCode
import itertools
x = [ 1, 4, 9, 16 ]
s = []
n = 9
#Remove elements >9
x = [ i for i in x if i <= n]
for i in xrange(1,n + 1):
for j in itertools.product(x,repeat = i):
if sum(j) == n:
s.append(list(j))
#Sort each combo
s =[sorted(i) for i in s]
#group by unique combo
print list(k for k,_ in itertools.groupby(s))
Result
>>>
>>>
[[9], [1, 4, 4], [1, 1, 1, 1, 1, 4], [1, 1, 1, 1, 1, 1, 1, 1, 1]]
EDIT
You can further optimize speed (if needed) by stopping finding combo's after sum of product is > 9
e.g.
if sum(j) > n + 2:
break
I'm working through this Kata and although I've looked through the solutions none are quite similar enough to mine to answer my question.
Problem Text: The number 89 is the first integer with more than one digit that fulfills the property partially introduced in the title of this kata. What's the use of saying "Eureka"? Because this sum gives the same number.
In effect: 89 = 8^1 + 9^2
The next number in having this property is 135.
See this property again: 135 = 1^1 + 3^2 + 5^3
We need a function to collect these numbers, that may receive two integers a, b that defines the range [a, b] (inclusive) and outputs a list of the sorted numbers in the range that fulfills the property described above.
def sum_dig_pow(a, b): # range(a, b + 1) will be studied by the function
# your code here
lst = []
n = 1
tot = 0
for i in range(a,b):
if i > 9:
spl = str(i).split()
for item in spl:
tot += int(item) ** n
n += 1
if tot == i:
lst.append(i)
else:
lst.append(i)
return lst
Tests are returning "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10] should equal [1, 2, 3, 4, 5, 6, 7, 8, 9, 89]".
I cannot figure out why it's passing 10 and not appending 89. I'm sure there's a more efficient way to do this as well but I'm still learning so want to be working in basics of loops, conditionals,etc.
This line is incorrect:
spl = str(i).split()
The split method will split a string on spaces by default and return a list. So passing i=10 gives back spl = ['10'], a list with one element. Instead, just iterate over each of the digits in the string.
for item in str(i):
...
Follow up: you can shorten your code by using enumerate to count the index of each digit.
def sum_dig_pow(a,b):
return [sum(int(y)**(i+1) for i,y in enumerate(str(x))) for x in range(a,b)]
Rather than spending a lot of time converting things from numbers to strings and back, try using arithmetic. To iterate over the digits of a number n, take n modulo ten (to get the least-significant digit) and then divide by ten (to peel off that least-significant digit). For example, the digits of 123 (in reverse order) are [(123 % 10), (12 % 10), (1 % 10)]
Thinking of it in terms of functions, first get the digits:
def digits_of_n(n):
result = []
while n > 0:
result.append(n % 10)
n = n / 10 # in python 3, use 3 // 10 for integer division
return reversed(result) # reverse list to preserve original order
then get the sum of the powers:
def sum_of_ith_powers(numbers):
result = 0
for i, n in enumerate(numbers): # the digits are ordered most-significant to least, as we would expect
result += n ** 1
return result
now you can just call sum_of_ith_powers(digits_of_n(n)) and you have an answer. If you like, you can give that operation a name:
def sum_of_digit_powers(n):
return sum_of_ith_powers(digits_of_n(n))
and you can then name the function that solves the kata:
def solve_kata(a, b):
return [sum_of_digit_powers(n) for n in range (a, b)]
You can use a generator, sum and enumerate in order to simplify your code like this example:
def sum_dig_pow(a,b):
for k in range(a,b+1):
if k > 9:
number_sum = sum(int(j)**i for i,j in enumerate(str(k), 1))
if k is number_sum:
yield k
else:
yield k
print(list(sum_dig_pow(1,10)))
print(list(sum_dig_pow(1,90)))
print(list(sum_dig_pow(1,10000)))
print(list(sum_dig_pow(10,1000)))
print(list(sum_dig_pow(1,900000)))
Output:
[1, 2, 3, 4, 5, 6, 7, 8, 9]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 89]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 89, 135, 175]
[89, 135, 175]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 89, 135, 175]
li = []
def sum_dig_pow(a, b):
for i in range(a, b+1):
sum1 = 0
for ind, val in enumerate(str(i), 1):
sum1 += pow(int(val), int(ind))
if i == sum1:
li.append(i)
return li
print(sum_dig_pow(1, 11))
Given a list of data as follows:
input = [1,1,1,1,5,5,3,3,3,3,3,3,2,2,2,5,5]
I would like to create an algorithm that is able to offset the list of certain number of steps. For example, if the offset = -1:
def offsetFunc(inputList, offsetList):
#make something
return output
where:
output = [0,0,0,0,1,1,5,5,5,5,5,5,3,3,3,2,2]
Important Note: The elements of the list are float numbers and they are not in any progression. So I actually need to shift them, I cannot use any work-around for getting the result.
So basically, the algorithm should replace the first set of values (the 4 "1", basically) with the 0 and then it should:
Detect the lenght of the next range of values
Create a parallel output vectors with the values delayed by one set
The way I have roughly described the algorithm above is how I would do it. However I'm a newbie to Python (and even beginner in general programming) and I have figured out time by time that Python has a lot of built-in functions that could make the algorithm less heavy and iterating. Does anyone have any suggestion to better develop a script to make this kind of job? This is the code I have written so far (assuming a static offset at -1):
input = [1,1,1,1,5,5,3,3,3,3,3,3,2,2,2,5,5]
output = []
PrevVal = 0
NextVal = input[0]
i = 0
while input[i] == NextVal:
output.append(PrevVal)
i += 1
while i < len(input):
PrevVal = NextVal
NextVal = input[i]
while input[i] == NextVal:
output.append(PrevVal)
i += 1
if i >= len(input):
break
print output
Thanks in advance for any help!
BETTER DESCRIPTION
My list will always be composed of "sets" of values. They are usually float numbers, and they take values such as this short example below:
Sample = [1.236,1.236,1.236,1.236,1.863,1.863,1.863,1.863,1.863,1.863]
In this example, the first set (the one with value "1.236") is long 4 while the second one is long 6. What I would like to get as an output, when the offset = -1, is:
The value "0.000" in the first 4 elements;
The value "1.236" in the second 6 elements.
So basically, this "offset" function is creating the list with the same "structure" (ranges of lengths) but with the values delayed by "offset" times.
I hope it's clear now, unfortunately the problem itself is still a bit silly to me (plus I don't even speak good English :) )
Please don't hesitate to ask any additional info to complete the question and make it clearer.
How about this:
def generateOutput(input, value=0, offset=-1):
values = []
for i in range(len(input)):
if i < 1 or input[i] == input[i-1]:
yield value
else: # value change in input detected
values.append(input[i-1])
if len(values) >= -offset:
value = values.pop(0)
yield value
input = [1,1,1,1,5,5,3,3,3,3,3,3,2,2,2,5,5]
print list(generateOutput(input))
It will print this:
[0, 0, 0, 0, 1, 1, 5, 5, 5, 5, 5, 5, 3, 3, 3, 2, 2]
And in case you just want to iterate, you do not even need to build the list. Just use for i in generateOutput(input): … then.
For other offsets, use this:
print list(generateOutput(input, 0, -2))
prints:
[0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 5, 5, 5, 3, 3]
Using deque as the queue, and using maxlen to define the shift length. Only holding unique values. pushing inn new values at the end, pushes out old values at the start of the queue, when the shift length has been reached.
from collections import deque
def shift(it, shift=1):
q = deque(maxlen=shift+1)
q.append(0)
for i in it:
if q[-1] != i:
q.append(i)
yield q[0]
Sample = [1.236,1.236,1.236,1.236,1.863,1.863,1.863,1.863,1.863,1.863]
print list(shift(Sample))
#[0, 0, 0, 0, 1.236, 1.236, 1.236, 1.236, 1.236, 1.236]
My try:
#Input
input = [1,1,1,1,5,5,3,3,3,3,3,3,2,2,2,5,5]
shift = -1
#Build service structures: for each 'set of data' store its length and its value
set_lengths = []
set_values = []
prev_value = None
set_length = 0
for value in input:
if prev_value is not None and value != prev_value:
set_lengths.append(set_length)
set_values.append(prev_value)
set_length = 0
set_length += 1
prev_value = value
else:
set_lengths.append(set_length)
set_values.append(prev_value)
#Output the result, shifting the values
output = []
for i, l in enumerate(set_lengths):
j = i + shift
if j < 0:
output += [0] * l
else:
output += [set_values[j]] * l
print input
print output
gives:
[1, 1, 1, 1, 5, 5, 3, 3, 3, 3, 3, 3, 2, 2, 2, 5, 5]
[0, 0, 0, 0, 1, 1, 5, 5, 5, 5, 5, 5, 3, 3, 3, 2, 2]
def x(list, offset):
return [el + offset for el in list]
A completely different approach than my first answer is this:
import itertools
First analyze the input:
values, amounts = zip(*((n, len(list(g))) for n, g in itertools.groupby(input)))
We now have (1, 5, 3, 2, 5) and (4, 2, 6, 3, 2). Now apply the offset:
values = (0,) * (-offset) + values # nevermind that it is longer now.
And synthesize it again:
output = sum([ [v] * a for v, a in zip(values, amounts) ], [])
This is way more elegant, way less understandable and probably way more expensive than my other answer, but I didn't want to hide it from you.