Confused about how python _for_ function works - python

I have a question about how the python function for works. I have a program I am writing which involved generating the prime factorization of several numbers.
for a in range(2,25):
print primefactorize(a)
When I tested it in the range above, the last list to pop up in the terminal was the prime factorization output for 24, not 25. Does the function
for a in range(1,x):
only run through each value up to x-1?
Thanks

I think your confusion is with range
range(2,25)
[2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24]
From the documentation:
This is a versatile function to create lists containing arithmetic
progressions. It is most often used in for loops. The arguments must
be plain integers. If the step argument is omitted, it defaults to 1.
If the start argument is omitted, it defaults to 0. The full form
returns a list of plain integers [start, start + step, start + 2 *
step, ...]. If step is positive, the last element is the largest start
+ i * step less than stop; if step is negative, the last element is the smallest start + i * step greater than stop. step must not be zero
(or else ValueError is raised). Example:
In math this is called an start inclusive, stop exclusive interval and looks like this:
[start, stop)

Related

Python -- Find a sequence of numbers in triplets, quadruplets or more:

So, in my problem i have a list of numbers,i.e:
[0, 10, 11, 12, 24, 26, 28, 30, 31]
and i want to find a sequence of consecutive numbers in this list, in this case [10, 11, 12] my desired output is a tuple telling me the position of the first number in this sequence and how many numbers are in the sequence, generalized for a sequence of N numbers.
the output for the given sequence is equal to (2,3)
I've tried something like for sequences of 3:
#sequence of 3
for i in range(len(b)-3):
if i>=2 and b[i]==b[i+1] and b[i]==b[i+2] and b[i]!=b[i-1] and b[i]!=b[i+3]:
count_seq_3 += 1
position_3= i
print(position_3, count_seq_3)
The given output is (0,0) anyone could please help me in what is wrong? thanks in advance!!
list = [0, 10, 11, 12, 24, 26, 28, 30, 31]
sequence = []
for i in range(len(list)-2):
if list[i] == list[i+1] - 1 or list[i] == list[i-1] + 1:
sequence.append(list[i])
print((list.index(sequence[0]), len(sequence)))
The actual output is:
(1, 3)
Where 1 is index of starting element 10 in your list and 3 is length of your sequence.

Iterating over a Python generator

I'm trying to write a function that will take as input a string of intervals e.g "0-0,4-8,20-21,43-45" to produce all numbers within each of the ranges meaning: [0, 4, 5, 6, 7, 8, 20, 21, 43, 44, 45]
The exercise requires to do so using generators. I managed to parse the input through a generator but I can't do the same for populating the numbers. I'm trying to int() each number so I could leverage the range() to produce all numbers within the edges.
Here's my conceptual code - how can I produce the numbers within each interval?
def parse_ranges(arg):
arg = arg.split(",")
parsed= (line.split("-") for line in arg)
#Trying to parse each character to int to use range()
intervals= (int(i) for i in number for number in parsed)
# Even if I had the characters parsed to int, I still don't know how to produce the range
ranges = (range(interval[0],interval[1]) interval for interval in intervals)
return ranges
print(list(parse_ranges("0-0,4-8,20-21,43-45")))
def parse_ranges(arg):
arg = arg.split(",")
parsed = ((line.split("-")) for line in arg)
for pair in parsed:
yield from range(int(pair[0]), int(pair[1])+1)
print(list(parse_ranges("0-0,4-8,20-21,43-45")))
Out: [0, 4, 5, 6, 7, 8, 20, 21, 43, 44, 45]
If you want to pass values from a generator INSIDE another generator directly out to the consumer of the outer generator, you need to use the "yield from" expression. (Also, note that you need to extend the "to" end of each range by +1, since the range endpoint is not inclusive.)
Two pieces that you seem to missing are: the second argument of range() needs to be one beyond what you want; you can pass control from one generator to another via yield from:
def parse_ranges(arg):
for start, stop in (interval.split('-') for interval in arg.split(',')):
yield from range(int(start), int(stop) + 1)
print(*parse_ranges("0-0,4-8,20-21,43-45"))
OUTPUT
% python3 test.py
0 4 5 6 7 8 20 21 43 44 45
%

Compare nums need optimisation (codingame.com)

www.codingame.com
Task
Write a program which, using a given number of strengths,
identifies the two closest strengths and shows their difference with an integer
Info
n = Number of horses
pi = strength of each horse
d = difference
1 < n < 100000
0 < pi ≤ 10000000
My code currently
def get_dif(a, b):
return abs(a - b)
horse_str = [10, 5, 15, 17, 3, 8, 11, 28, 6, 55, 7]
n = len(horse_str)
d = 10000001
for x in range(len(horse_str)):
for y in range(x, len(horse_str) - 1):
d = min([get_dif(horse_str[x], horse_str[y + 1]), d])
print(d)
Test cases
[3,5,8, 9] outputs: 1
[10, 5, 15, 17, 3, 8, 11, 28, 6, 55, 7] outputs: 1
Problem
They both work but then the next test gives me a very long list of horse strengths and i get **Process has timed out. This may mean that your solution is not optimized enough to handle some cases.
How can i optimise it? Thank you!
EDIT ONE
Default code given
import sys
import math
# Auto-generated code below aims at helping you parse
# the standard input according to the problem statement.
n = int(input())
for i in range(n):
pi = int(input())
# Write an action using print
# To debug: print("Debug messages...", file=sys.stderr)
print("answer")
Since you can use sort method (which is optimized to avoid performing a costly bubble sort or double loop by hand which has O(n**2) complexity, and times out with a very big list), let me propose something:
sort the list
compute the minimum of absolute value of difference of the adjacent values, passing a generator comprehension to the min function
The minimum has to be the abs difference of adjacent values. Since the list is sorted using a fast algorithm, the heavy lifting is done for you.
like this:
horse_str = [10, 5, 15, 17, 3, 8, 11, 28, 6, 55, 7]
sh = sorted(horse_str)
print(min(abs(sh[i]-sh[i+1]) for i in range(len(sh)-1)))
I also get 1 as a result (I hope I didn't miss anything)

Moving odd numbers to the end of a list in Python, strange outcome [duplicate]

This question already has answers here:
Strange result when removing item from a list while iterating over it
(8 answers)
Closed 7 years ago.
I'm trying to move the odd numbers in this list to the end without using an external list. When I run the code, it moves half of the odd numbers over, and leaves the other half where they were. The overall output is correct if I run the code twice on the list, but I should only have to run it once, right? What am I doing wrong?
a = [3, 4, 55, 13, 6, 19, 33, 10, 11, 45]
for ind, val in enumerate(a):
if val % 2 != 0:
a.pop(ind)
a.append(val)
print a
Thanks.
This is because, as a general rule, you shouldn't iterate through and modify the same list at the same time. The indices get thrown off!
As you go through and modify the list, not all of the elements actually get cycled through. The a that you are popping is a different list with different indicies after your first change to the list, but you are still using the enumeration of the original version for your loop.
You could use pythons sorting methods (sorted() or someList.sort()) and pass a special key function to it:
>>> sorted(a, key = lambda element: element%2 != 0)
[4, 6, 10, 3, 55, 13, 19, 33, 11, 45]
This is possible because Python sorts are guaranteed to be stable und therefore when multiple records have the same key, their original order is preserved.
Your approach has two problems,
you modify the list on which you iterate
you don't test if the number that slides into the position occupied by an odd number is odd itself
To obviate with 1., you simply can iterate on the indices of your list but to obviate 2., you must start from the end of the list and going towards the beginning, using negative indices.
Executing the following code
a = [3, 4, 55, 13, 6, 19, 33, 10, 11, 45]
for i in range(len(a)):
j = -1-i
if a[j]%2:
a.append(a[j]) ; a.pop(j-1) # j-1 because our list is temporarily longer...
print a
gives you the following output
[4, 6, 10, 45, 11, 33, 19, 13, 55, 3]

Sample k random permutations without replacement in O(N)

I need a number of unique random permutations of a list without replacement, efficiently. My current approach:
total_permutations = math.factorial(len(population))
permutation_indices = random.sample(xrange(total_permutations), k)
k_permutations = [get_nth_permutation(population, x) for x in permutation_indices]
where get_nth_permutation does exactly what it sounds like, efficiently (meaning O(N)). However, this only works for len(population) <= 20, simply because 21! is so mindblowingly long that xrange(math.factorial(21)) won't work:
OverflowError: Python int too large to convert to C long
Is there a better algorithm to sample k unique permutations without replacement in O(N)?
Up to a certain point, it's unnecessary to use get_nth_permutation to get permutations. Just shuffle the list!
>>> import random
>>> l = range(21)
>>> def random_permutations(l, n):
... while n:
... random.shuffle(l)
... yield list(l)
... n -= 1
...
>>> list(random_permutations(l, 5))
[[11, 19, 6, 10, 0, 3, 12, 7, 8, 16, 15, 5, 14, 9, 20, 2, 1, 13, 17, 18, 4],
[14, 8, 12, 3, 5, 20, 19, 13, 6, 18, 9, 16, 2, 10, 4, 1, 17, 15, 0, 7, 11],
[7, 20, 3, 8, 18, 17, 4, 11, 15, 6, 16, 1, 14, 0, 13, 5, 10, 9, 2, 19, 12],
[10, 14, 5, 17, 8, 15, 13, 0, 3, 16, 20, 18, 19, 11, 2, 9, 6, 12, 7, 4, 1],
[1, 13, 15, 18, 16, 6, 19, 8, 11, 12, 10, 20, 3, 4, 17, 0, 9, 5, 2, 7, 14]]
The odds are overwhelmingly against duplicates appearing in this list for len(l) > 15 and n < 100000, but if you need guarantees, or for lower values of len(l), just use a set to record and skip duplicates if that's a concern (though as you've observed in your comments, if n gets close to len(l)!, this will stall). Something like:
def random_permutations(l, n):
pset = set()
while len(pset) < n:
random.shuffle(l)
pset.add(tuple(l))
return pset
However, as len(l) gets longer and longer, random.shuffle becomes less reliable, because the number of possible permutations of the list increases beyond the period of the random number generator! So not all permutations of l can be generated that way. At that point, not only do you need to map get_nth_permutation over a sequence of random numbers, you also need a random number generator capable of producing every random number between 0 and len(l)! with relatively uniform distribution. That might require you to find a more robust source of randomness.
However, once you have that, the solution is as simple as Mark Ransom's answer.
To understand why random.shuffle becomes unreliable for large len(l), consider the following. random.shuffle only needs to pick random numbers between 0 and len(l) - 1. But it picks those numbers based on its internal state, and it can take only a finite (and fixed) number of states. Likewise, the number of possible seed values you can pass to it is finite. This means that the set of unique sequences of numbers it can generate is also finite; call that set s. For len(l)! > len(s), some permutations can never be generated, because the sequences that correspond to those permutations aren't in s.
What are the exact lengths at which this becomes a problem? I'm not sure. But for what it's worth, the period of the mersenne twister, as implemented by random, is 2**19937-1. The shuffle docs reiterate my point in a general way; see also what Wikipedia has to say on the matter here.
Instead of using xrange simply keep generating random numbers until you have as many as you need. Using a set makes sure they're all unique.
permutation_indices = set()
while len(permutation_indices) < k:
permutation_indices.add(random.randrange(total_permutations))
I had one implementation of nth_permutation (not sure from where I got it) which I modified for your purpose. I believe this would be fast enough to suit your need
>>> def get_nth_permutation(population):
total_permutations = math.factorial(len(population))
while True:
temp_population = population[:]
n = random.randint(1,total_permutations)
size = len(temp_population)
def generate(s,n,population):
for x in range(s-1,-1,-1):
fact = math.factorial(x)
d = n/fact
n -= d * fact
yield temp_population[d]
temp_population.pop(d)
next_perm = generate(size,n,population)
yield [e for e in next_perm]
>>> nth_perm = get_nth_permutation(range(21))
>>> [next(nth_perm) for k in range(1,10)]
You seem to be searching for the Knuth Shuffle! Good luck!
You could use itertools.islice instead of xrange():
CPython implementation detail: xrange() is intended to be simple and
fast Implementations may impose restrictions to achieve this. The C
implementation of Python restricts all arguments to native C longs
(“short” Python integers), and also requires that the number of
elements fit in a native C long. If a larger range is needed, an
alternate version can be crafted using the itertools module:
islice(count(start, step), (stop-start+step-1+2*(step<0))//step).

Categories