Use a custom order in range function Python - python

I have been struggling with this quite a bit, and was wondering if there is a solution for this.
I would like to use the range(start,stop,step) function in Python, but I would like to use a different order than what the normal functionalities allow. From what I understand the range function can do 3 things:
0, 1, 2, 3 etc
10, 9, 8, 7 etc
2, 4, 6, 8 etc
Now I am looking for the following order:
0, 10, 1, 9, 2, 8, 3, 7 etc
In this case 10 in the len(df), so the last row of the df.

You can create a generator to do that:
def my_range(n):
"""Yields numbers from 0 to n, in order 0, n, 1, n-1..."""
low = 0
high = n
while low <= high:
yield low
if high != low:
yield high
low += 1
high -= 1
Some examples:
print(list(my_range(10)))
# [0, 10, 1, 9, 2, 8, 3, 7, 4, 6, 5]
for i in my_range(5):
print(i)
0
5
1
4
2
3
This will provide every number in the interval exactly once, and lazily, just as range.
To answer the question in your comment:
if you want to mix the numbers in your_list = [1,3,4,5,7,9,10,12,13,14], you can just use this function to generate the indices:
your_list = [1,3,4,5,7,9,10,12,13,14]
for index in my_range(len(your_list)-1):
print(your_list[index], end=' ')
# 1 14 3 13 4 12 5 10 7 9
or you could build a new list in the mixed order:
new = [your_list[index] for index in my_range(len(your_list)-1)]
print(new)
# [1, 14, 3, 13, 4, 12, 5, 10, 7, 9]

range just has start, stop, step params. But you can achieve what you want by zipping two ranges together along with the chain.from_iterable function:
from itertools import chain
for val in chain.from_iterable(zip(range(11), range(10, -1, -1))):
print(val)
# 0 10 1 9 2 8 3 7 4 6 5 5 6 4 7 3 8 2 9 1 10 0
Note this solution repeats values, if you want no repeated values, then a generator is the way to go.

To keep things simple and clear the range function cannot do this.
Since it only allows to either increment or decrement at once.
but this can be achieved with loops and variables.
this is how to do it
for i in range(0,11):
print(i,10-i,end=" ")
this code will do it.

Related

Array rotation(Juggling method) in python - List Index out of range

Array rotation can be done by slicing in python or even some other easier way too. But, I found a method in GFG - which has the following procedure to rotate an array:
Instead of moving one by one, divide the array into different sets where a number of sets are equal to GCD of n and d and move the elements within sets.
If GCD is 1, then, for example, array (n = 7 and d =2), then elements will be moved within one set only, we just start with temp = arr[0] and keep moving arr[I+d] to arr[I] and finally store temp at the right place.
Here is an example for n =12 and d = 3. GCD is 3 and
Let arr[] be {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}
a) Elements are first moved in first set –
arr[] after this step --> {4 2 3 7 5 6 10 8 9 1 11 12}
b)Then in second set.
arr[] after this step --> {4 5 3 7 8 6 10 11 9 1 2 12}
c)Finally in the third set.
arr[] after this step --> {4 5 6 7 8 9 10 11 12 1 2 3}
I tried to implement this method in python:
arr = [9,8,0,3,4,5,6,7,8]
d=3
for i in range(d):
for j in range(i,len(arr),d):
if j+1 == len(arr)-d+(i+1):
break
temp = arr[j]
arr[j]=arr[j+d]
arr[j+d] = temp
print(arr)
I don't know where I have gone wrong with the code. For this particular array as well as d value(rotation value) - I get the perfect output : [3, 4, 5, 6, 7, 8, 9, 8, 0]
But, when I give the value of d=2,
I get this error:
File ".\array_rotation.py", line 114, in <module>
arr[j]=arr[j+d]
IndexError: list index out of range
The same goes for different arrays too, I get a correct answer for the array according to the 'd' value.
The other array: arr = [1,2,3,4,5,6,7,8,9,10,11,12], d=2 -> I get perfect answer: [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 1, 2]
But with d=5 -> Again, I get list index out of range error.
I don't know where I have gone wrong - some test cases work and some do not.

Python range with fixed number of elements and fixed interval

I need to create a range like this in Pyhton:
1 ... 4 ... 7 ... 10 ... 13 ... 16 ...
But I would like to estabilish not the end of range, but the number of elements.
For example:
range_num_of_elements(1, num_of_elements=4, interval=3)
Gives as result:
[1, 4, 7, 10]
How can I do it?
EDIT: this question
Creating a range with fixed number of elements (length)
Doesn't answers my question. I wanna specify start, interval, num, where the question above specifies start, end, num.
you could define your range just like this:
def my_range(start, num_elements, step):
return range(start, start+step*num_elements, step)
list(my_range(1, 4, 3))
# [1, 4, 7, 10]
this would have all the nice features of range; e.g.:
7 in my_range(1, 4, 3) # True
8 in my_range(1, 4, 3) # False
You can use a list comprehension:
[start + interval * n for n in range(num_of_elements)]
Where
start = 1
interval = 3
num_of_elements = 4
This will give
[1, 4, 7, 10]
Or you can just compute the appropriate arguments to range, as Tom Karzes suggested in the comments:
range(start, start + interval * num_of_elements, interval)

Eliminating Consecutive Numbers

If you have a range of numbers from 1-49 with 6 numbers to choose from, there are nearly 14 million combinations. Using my current script, I currently have only 7.2 million combinations remaining. Of the 7.2 million remaining combinations, I want to eliminate all 3, 4, 5, 6, dual, and triple consecutive numbers.
Example:
3 consecutive: 1, 2, 3, x, x, x
4 consecutive: 3, 4, 5, 6, x, x
5 consecutive: 4, 5, 6, 7, 8, x
6 consecutive: 5, 6, 7, 8, 9, 10
double separate consecutive: 1, 2, 5, 6, 14, 18
triple separate consecutive: 1, 2, 9, 10, 22, 23
Note: combinations such as 1, 2, 12, 13, 14, 15 must also be eliminated or else they conflict with the rule that double and triple consecutive combinations to be eliminated.
I'm looking to find how many combinations of the 7.2 million remaining combinations have zero consecutive numbers (all mixed) and only 1 consecutive pair.
Thank you!
import functools
_MIN_SUM = 120
_MAX_SUM = 180
_MIN_NUM = 1
_MAX_NUM = 49
_NUM_CHOICES = 6
_MIN_ODDS = 2
_MAX_ODDS = 4
#functools.lru_cache(maxsize=None)
def f(n, l, s = 0, odds = 0):
if s > _MAX_SUM or odds > _MAX_ODDS:
return 0
if n == 0 :
return int(s >= _MIN_SUM and odds >= _MIN_ODDS)
return sum(f(n-1, i+1, s+i, odds + i % 2) for i in range(l, _MAX_NUM+1))
result = f(_NUM_CHOICES, _MIN_NUM)
print('Number of choices = {}'.format(result))
While my answer should work, I think someone might be able to offer a faster solution.
Consider the following code:
not_allowed = []
for x in range(48):
not_allowed.append([x, x+1, x+2])
# not_allowed = [ [0,1,2], [1,2,3], ... [11,12,13], ... [47,48,49] ]
my_numbers = [[1, 2, 5, 9, 11, 33], [1, 3, 7, 8, 9, 31], [12, 13, 14, 15, 23, 43]]
for x in my_numbers:
for y in not_allowed:
if set(y) <= set(x): # if [1,2,3] is a subset of [1,2,5,9,11,33], etc.
# drop x
This code will remove all instances that contain double consecutive numbers, which is all you really need to check for, because triple, quadruple, etc. all imply double consecutive. Try implementing this and let me know how it works.
The easiest approach is probably to generate and filter. I used numpy to try to vectorize as much of this as I could:
import numpy as np
from itertools import combinations
combos = np.array(list(combinations(range(1, 50), 6))) # build all combos
# combos is shape (13983816, 6)
filt = np.where(np.bincount(np.where(np.abs(
np.subtract(combos[:, :-1], combos[:, 1:])) == 1)[0]) <= 1)[0] # magic!
filtered = combos[filt]
# filtered is shape (12489092, 6)
Breaking down that "magic" line
First we subtract the first five items in the list from the last five items to get the differences between them. We do this for the entire set of combinations in one shot with np.subtract(combos[:, :-1], combos[:, 1:]). Note that itertools.combinations produces sorted combinations, on which this depends.
Next we take the absolute value of these differences to make sure we only look at positive distances between numbers with np.abs(...).
Next we grab the indicies from this operation for the entire dataset that indicate a difference of 1 (consecutive numbers) with np.where(... == 1)[0]. Note that np.where returns a tuple where the first item are all of the rows, and the second item are all of the corresponding columns for our condition. This is important because any row value that shows up more than once tells us that we have more than one consecutive number in that row!
So we count how many times each row shows up in our results with np.bincount(...), which will return something like [5, 4, 4, 4, 3, 2, 1, 0] indicating how many consecutive pairs are in each row of our combinations dataset.
Finally we grab only the row numbers where there are 0 or 1 consecutive values with np.where(... <= 1)[0].
I am returning way more combinations than you seem to indicate, but I feel fairly confident that this is working. By all means, poke holes in it in the comments and I will see if I can find fixes!
Bonus, because it's all vectorized, it's super fast!

Infinite cycle over a range starting at a particular number

Say I have a range:
r = range(1, 6)
Using this range, I want to cycle infinitely and yield the numbers as they come:
for i in cycle(r):
yield(i)
This would correctly produce values of:
1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, ...
However, I sometimes want to start the yielding from a specific value but continue on with the range as it's defined. That is, if I want to start at 3, the sequence would be:
3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, ...
Is there a way to do this with a combination of range and cycle (or some other way)?
Just dropwhile until you reach the first value you want to emit:
>>> from itertools import cycle, dropwhile
>>> iterable = dropwhile(lambda x: x < 3, cycle(range(1, 6)))
>>> for _ in range(10):
... print(next(iterable))
...
3
4
5
1
2
3
4
5
1
2
Per the docs (emphasis mine):
Make an iterator that drops elements from the iterable as long as the
predicate is true; afterwards, returns every element.
The predicate only takes effect until the first value for which it evaluates false-y.
Since cycle starts from the beginning of the iterator given to it, give it an iterator (a sequence in this case) that starts where you want:
r = tuple(range(3,6)) + tuple(range(1,3))
The 1 above is the lowest value to repeat, the 6 is one more than the highest value, and the 3 used twice is the starting value. It should be clear how to generalize this to other cases.
Another way to do this is to chain two ranges together and pass the chain to cycle.
from itertools import cycle, chain
def shift_cycle(lo, start, stop):
return cycle(chain(range(start, stop), range(lo, start)))
for t in zip(range(12), shift_cycle(1, 3, 6)):
print('{}: {}'.format(*t))
output
0: 3
1: 4
2: 5
3: 1
4: 2
5: 3
6: 4
7: 5
8: 1
9: 2
10: 3
11: 4
This approach has an advantage over islice or dropwhile if the start and stop args are large because it doesn't need to discard the unwanted initial items.
perhaps unesthetic but practical? sys.maxsize being "practically infinite" for many purposes
import sys
r, n = 5, 3
cyc = (i%r + 1 for i in range(n, sys.maxsize))
next(cyc)
Out[106]: 4
next(cyc)
Out[107]: 5
next(cyc)
Out[108]: 1
next(cyc)
Out[109]: 2
next(cyc)
Out[110]: 3
next(cyc)
Out[111]: 4
next(cyc)
Out[112]: 5
next(cyc)
Out[113]: 1
sys.maxsize*1e-9/3600/24/365
Out[117]: 292.471208677536
that's years at 1 ns per request - on a 64 bit system
but of course it runs a bit slower
timeit.timeit('next(cyc)','r, n = 5, 3; cyc = (i%r + 1 for i in range(n, sys.maxsize))')
Out[126]: 0.2556792100261305
the modulo takes more time as requests keeps upping i
but that doesn't seem to be the big time sink
timeit.timeit('max%5', 'max=sys.maxsize')
Out[120]: 0.07545763840474251
timeit.timeit('1111%5')
Out[122]: 0.01156394737682831
timeit.timeit('111%5')
Out[123]: 0.011720469965638358
you are looking for the islice function
from itertools import islice, cycle
offset = 2
r = range(1, 6)
generator = islice(cycle(r), offset, None)

How can i do this simple thing in Python from Matlab?

Simple Matlab code: e.g A(5+(1:3)) -> gives [A(6), A(7), A(8)]
In the above, A is a vector or a matrix. For instance:
A = [1 2 3 4 5 6 7 8 9 10];
A(5+(1:3))
ans =
6 7 8
Note that MATLAB indexing starts at 1, not 0.
How can i do the same in Python?
You are looking for slicing behavior
A = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> A[5:8]
[6, 7, 8]
If A is some function that you want to call with parameters 6, 7, and 8, you could use a list comprehension.
answers = [A(6+i) for i in range(3)]
You want to do two things.
First, create a range (5 + (1:3)) which could be done in Python like range(number).
Second, apply a function to each range index. This could be done with map or a for loop.
The for loop solutions have been addressed, so here's a map based one:
result = map(A,your_range)
Use a list comprehension:
x = 5
f = 1 # from
t = 3 # till
print [x+i for i in range(f,t+1)]
If you are trying to use subscripts to create an array which is a subset of the whole array:
subset_list = A[6:8]
in python u can do it easily by A[5:5+3] . u can reference the values 5 and 3 by variables also like
b=5
c=3
a[b:b+c]

Categories

Resources