Python Ruler Sequence Generator - python

I have been struggling for a long time to figure how to define a generator function of a ruler sequence in Python, that follows the rules that the first number of the sequence (starting with 1) shows up once, the next two numbers will show up twice, next three numbers will show up three times, etc.
So what I am trying to get is 1, 2, 2, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7, 7 etc.
I understand that the way to do this is to have two separate count generators (itertools.count(1)) and then for every number in one generator yield number from the other generator:
def rul():
num = itertools.count(1)
repeator = itertools.count(1)
for x in range(next(repeator)):
yield from num
But if I hit next() on this function, I get back just the regular 1,2,3,4.. sequence...
Any help on this would be appreciated.

how about regular old python with no itertools?
def ruler():
counter = 1
n = 1
while True:
for i in range(counter):
for j in range(counter):
yield n
n += 1
counter += 1
in my humble opinion this is the clearest and most straighforward solution for these types of situations

How about itertools.repeat?
import itertools
def ruler():
num = rep_count = 0
while True:
rep_count += 1
for i in range(rep_count):
num += 1
yield from itertools.repeat(num, rep_count)

You can obtain such a generator without writing your own function using count() and repeat() from itertools:
from itertools import repeat,count
i = count(1,1)
rul = (n for r in count(1,1) for _ in range(r) for n in repeat(next(i),r))
for n in rul: print(n, end = " ")
# 1 2 2 3 3 4 4 4 5 5 5 6 6 6 7 7 7 7 8 8 8 8 9 9 9 9 10 10 10 10 11 11 ...

If you want to go all in on itertools, you'll need count, repeat, and chain.
You can group the numbers in your sequence as follows, with
each group corresponding to a single instance of repeat:
1 # repeat(1, 1)
2 2 # repeat(2, 2)
3 3 # repeat(3, 2)
4 4 4 # repeat(4, 3)
5 5 5 # repeat(5, 3)
6 6 6 # repeat(6, 3)
7 7 7 7 # repeat(7, 4)
...
So we can define ruler_numbers = chain.from_iterable(map(repeat, col1, col2)), as long as we can define col1 and col2 appropriately.
col1 is easy: it's just count(1).
col2 is not much more complicated; we can group them similarly to the original seqeunce:
1 # repeat(1, 1)
2 2 # repeat(2, 2)
3 3 3 # repeat(3, 3)
4 4 4 4 # repeat(4, 4)
...
which we can also generate using chain.from_iterable and map:
chain.from_iterable(map(repeat, count(1), count(1))).
In the end, we get our final result in our best attempt at writing Lisp in Python :)
from itertools import chain, repeat, count
ruler_numbers = chain.from_iterable(
map(repeat,
count(1),
chain.from_iterable(
map(repeat,
count(1),
count(1)))))
or if you want to clean it up a bit with a helper function:
def concatmap(f, *xs):
return chain.from_iterable(map(f, *xs))
ruler_numbers = concatmap(repeat,
count(1),
concatmap(repeat,
count(1),
count(1)))

Related

iterate every two elements in the list to do something of the list python

I have a list named newList and inside the list there is another list of information with JSON code. I want to iterate every two elements in the newList to do something for those two elements.
ex: if my newList length is 8, I want to able do a for loop or access the 0 and 1 element, do something with the information inside those two elements, then go to 1 and 2 elements and do something with those elements.
My code:
data = np.arange(len(newList))
def pairwise(iterable):
a, b = tee(iterable)
next(b, None)
return zip(a, b)
for v, w in pairwise(data):
print(v, w)
print result:
0 1
1 2
2 3
3 4
4 5
5 6
6 7
7 8
8 9
9 10
10 11
11 12
12 13
13 14
14 15
What I am looking for:
0 1
2 3
4 5
6 7
8 9
10 11
12 13
14 15
You can create a single iterator and then zip it with itself:
def pairwise(iterable):
return zip(*[iter(iterable)]*2)
which is similar to:
def pairwise(iterable):
i = iter(iterable)
return zip(i, i)
Do you want (0, 1), (1, 2) or (0, 1), (2, 3)? Your question seems to say you want one, and it seems to say you want the other.
Anyway, here's some code for the latter:
#!/usr/local/cpython-3.8/bin/python3
import itertools
def test_data(n):
# Yes, there's a simpler way of doing this.
for i in range(n):
yield i
def two_up(iterable):
left_iterable, right_iterable = itertools.tee(iterable)
left_every_other = itertools.islice(left_iterable, 0, None, 2)
right_every_other = itertools.islice(right_iterable, 1, None, 2)
for tuple_ in zip(left_every_other, right_every_other):
yield tuple_
def main():
it = test_data(10)
for thing in two_up(it):
print(thing)
main()

How to print a sequence stacked in a "pyramid" with alternating sort?

I want to print the following sequence of integers in a pyramid (odd rows sorted ascending, even rows sorted descending). If S=4, it must print four rows and so on.
Expected output:
1
3 2
4 5 6
10 9 8 7
I tried out the following code but it produced the wrong output.
S=int(input())
for i in range(1,S+1):
y=i+(i-1)
if i%2!=0:
print(*range(i,y+1))
elif i%2==0:
print(*range(y,i-1,-1))
# Output:
# 1
# 3 2
# 3 4 5
# 7 6 5 4
You need some way of either keeping track of where you are in the sequence when printing each row, generating the entire sequence and then chunking it into rows, or... (the list of possible approaches goes on and on).
Below is a fairly simple approach that just keeps track of a range start value, calculates the range stop value based on the row number, and reverses even rows.
rows = int(input())
start = 1
for n in range(1, rows + 1):
stop = int((n * (n + 1)) / 2) + 1
row = range(start, stop) if n % 2 else reversed(range(start, stop))
start = stop
print(*row)
# If rows input is 4, then output:
# 1
# 3 2
# 4 5 6
# 10 9 8 7
Using itertools.count and just reversing the sublist before printing on even rows
from itertools import count
s = 4
l = count(1)
for i in range(1, s+1):
temp = []
for j in range(i):
temp.append(next(l))
if i % 2:
print(' '.join(map(str, temp)))
else:
print(' '.join(map(str, temp[::-1])))
1
3 2
4 5 6
10 9 8 7

How can I combine binary a set of data

I have a set of values vertically
2,4
4,7
5,8
9
I want to have binary combination of each two values in vertical for example 2 and 4, 2 and 5 etc . the same for the second
2 4
2 5
4 5
4 7
4 8
.
.
.
Ok it seems very complicated so I try to make it easier
I convert my data into horizon
I have
2,4,5
4,7,8,9
I want to have the binary combination of the first row
2 4
2 5
4 5
and the the binary combination of the second row
4 7
4 8
4 9
7 8
7 9
8 9
If you have values stores in two collectables, use list comprehension
from itertools import izip_longest
a = [(1,'a'),(2,'b'),(3,None)]
b,c = izip_longest(*a)
d = [(i, j) for i in b if i for j in c if j]
I think I understand. Try this code:
test.py
#!/bin/python
# put items side by side
# take first item and put the next item besides it
# if there are any more items after the next, put that item besides the first item
# if there are no more items after the next, switch to the next item in the list
# repeat
def two_items_side_by_side(mylist):
list_len = len(mylist)
for i in range(list_len):
for j in range(i+1, list_len):
print '{} {}'.format(mylist[i], mylist[j])
# -------------------------------------------------------------------
# these are two lists
list1 = [2, 4, 5]
list2 = [4, 7, 8, 9]
two_items_side_by_side(list1)
two_items_side_by_side(list2)
When you run this, your results will look like so:
Result
python test.py
2 4
2 5
4 5
4 7
4 8
4 9
7 8
7 9
8 9
If your test case is a string with each line containing comma separated text like this, you can use test2.py as an example
2,4
4,7
5,8
,9
test2.py
#!/bin/python
# put items side by side
# take first item and put the next item besides it
# if there are any more items after the next, put that item besides the first item
# if there are no more items after the next, switch to the next item in the list
# repeat
def two_items_side_by_side(mylist):
list_len = len(mylist)
for i in range(list_len):
for j in range(i+1, list_len):
print '{} {}'.format(mylist[i], mylist[j])
# -------------------------------------------------------------------
# process the data and store them into a list
# then do the same work as we did in the first example
def convert_data_into_lists():
lines = data.split('\n')
for line in lines:
# ignore empty lines
if len(line.strip()) < 1:
continue
# split by comma and ignore if we don't get 2 or more values
items = line.split(',')
if len(items) < 2:
continue
# put first item in list1 and second item in list2
if len(items[0].strip()) > 0: list1.append(items[0].strip())
if len(items[1].strip()) > 0: list2.append(items[1].strip())
# -------------------------------------------------------------------
# this is my string
data = """
2,4
4,7
5,8
,9
"""
list1 = []
list2 = []
convert_data_into_lists()
two_items_side_by_side(list1)
two_items_side_by_side(list2)
Result
python test2.py
2 4
2 5
4 5
4 7
4 8
4 9
7 8
7 9
8 9
There are more elegant ways to write this code. I have written it in a manner that will help you understand the code and try it out yourself.
Requirement change
Based on the change in requirement, data is in a text file. We will take three test cases (see in results). To accommodate the requirements, I am going to use the same code I used in test2.py. Instead of creating individual lists for each column we have in our text file, I will create one list that will dynamically contain as many lists as you have columns in your text file.
Code
#!/bin/python
# put items side by side
# take first item and put the next item besides it
# if there are any more items after the next, put that item besides the first item
# if there are no more items after the next, switch to the next item in the list
# repeat
def two_items_side_by_side(mylist):
list_len = len(mylist)
for i in range(list_len):
for j in range(i+1, list_len):
print '{} {}'.format(mylist[i], mylist[j])
# -------------------------------------------------------------------
# process the data and store them into a list
# then do the same work as we did in the first example
def convert_data_into_lists():
with open(data) as f:
lines = f.readlines()
for line in lines:
# ignore empty lines
if len(line.strip()) < 1:
continue
# split by comma and ignore if we don't get 2 or more values
items = line.split(',')
counter = 0
for item in items:
if len(mylist) < counter + 1:
mylist.append([])
if len(item.strip()) > 0:
mylist[counter].append(item.strip())
counter += 1
# -------------------------------------------------------------------
# this is my string
data = 'test.txt'
mylist = []
convert_data_into_lists()
for individual_list in mylist:
two_items_side_by_side(individual_list)
Result
Case 1
Data:
2,4
4,7
5,8
,9
Results:
2 4
2 5
4 5
4 7
4 8
4 9
7 8
7 9
8 9
Case 2
Data:
2,4
4,7
5,8
6,9
Results:
2 4
2 5
2 6
4 5
4 6
5 6
4 7
4 8
4 9
7 8
7 9
8 9
Case 3
Data:
2,4,10
4,7,11
5,8,
,9,13
Results:
2 4
2 5
2 6
4 5
4 6
5 6
4 7
4 8
4 9
7 8
7 9
8 9
10 11
10 13
11 13
EDIT
By modifying the code above to only use a single parameter, we can read the contents of a csv file and (using some form of delimiter) provide combinations across the entire data set. Just call total_zipper() and replace 'filename.txt' with your file name.
def total_zipper():
def zipper(a):
lst = []
for i in range(1,len(a)+1):
lst+=zip(a,a[i:])
return sorted(lst)
f = open('filename.txt','r')
return [zipper(line) for line in f]
This treats all lines as iterables (Strings). For readline() to work, I believe you need a return statement at the end of each line in the txt. See the input/output page for Python for more.
Here's the shortest version I could come up with. You can use the built-in zip() function. This, when combined with list slicing, results in a pythonic way to pair the values in the required order.
def zipper(a,b):
lst = []
for i in range(1,len(b)+1):
lst+=zip(a,b[i:])
return sorted(lst)
Now simply call zipper on the various rows of data.
>>> a = [2,4,5]
>>> b = [4,7,8,9]
>>> print(zipper(a,a))
[(2, 4), (2, 5), (4, 5)]
>>> print(zipper(b,b))
[(4, 7), (4, 8), (4, 9), (7, 8), (7, 9), (8, 9)]
As a side note, I tried to use list comprehension to make the code shorter. For example, the following code does the same thing as zipper(a):
def zipper(a):
return list(zip(a,a[i:]) for i in range(1,len(a)+1))
However, with zip() returning generator objects in Python 3, the results aren't as "clean" as the output from the version above. I'd have to use next() on each generator object in the list outputted by zipper in order to get the same output, but this is a tedious process. Anyone have suggestions for making the list comprehension work?

Keeping Python from spacing after breaking a line when printing a List

(yes, I've searched all around for a solution, and, if did I see it, I wasn't able to relate to my issue. I'm new to Python, sorry!)
I've got a work to do, and it says to me:
"User will input X and Y. Show a sequence from 1 to Y, with only X elements each line."
e.g
2 4 as entrance
1 2
3 4
e.g 2 6
1 2
3 4
5 6
Okay... So, I thought on doing this:
line, final = input().split()
line = int(line)
final = int(final)
List = []
i = 0
total = (final // line)
spot = 0
correction = 0
k = 1
if i != final:
List = list(range(1, final + 1, 1))
i += 1
while k != total:
spot = line * k + correction
correction += 1
k += 1
list.insert(List, spot, '\n')
print(*List)
Ok. So I managed to build my List from 1 to the "final" var.
Also managed to find on which spots (therefore, var "spot") my new line would be created. (Had to use a correction var and some math to reach it, but it's 10/10)
So far, so good.
The only problem is this work is supposed to be delivered on URI Online Judge, and it DEMANDS that my result shows like this:
2 10 as entrance
1 2
3 4
5 6
7 8
9 10
And, using the code I just posted, I get this as a result:
1 2
3 4
5 6
7 8
9 10
Thus, it says my code is wrong. I've tried everything to remove those spaces (I think). Using sys won't work since it only prints one argument. Tried using join (but I could have done it wrong, as I'm new anyway)
Well, I've tried pretty much anything. Hope anyone can help me.
Thanks in advance :)
You have built a list that includes each necessary character, including the linefeed. Therefore, you have a list like this:
[1, 2, '\n', 3, 4, '\n'...]
When you unpack arguments to print(), it puts a separator between each argument, defaulting to a space. So, it prints 1, then a space, then 2, then a space, then a linefeed, then a space... And that is why you have a space at the beginning of each line.
Instead of inserting linefeeds into a list, chunk that list with iter and next:
>>> def chunks(x, y):
... i = iter(range(1, y+1))
... for row in range(y//x):
... print(*(next(i) for _ in range(x)))
... t = tuple(i)
... if t:
... print(*t)
...
>>> chunks(2, 6)
1 2
3 4
5 6
>>> chunks(2, 7)
1 2
3 4
5 6
7
The problem with the approach you're using is a result of a space being printed after each "\n" character in the series. While the idea was quite clever, unfortunately, I think this means you will have to take a different approach from inserting the newline character into the list.
Try this approach: (EDITED)
x, y = input().split()
x, y = int(x), int(y)
for i in range(1, y+1):
if i % x == 0 or i == y:
print(i)
else:
print(i, end=" ")
Output for 3 11
1 2 3
4 5 6
7 8 9
10 11
Output for 2 10
1 2
3 4
5 6
7 8
9 10
Use itertools to take from an iterable in chunks:
>>> import itertools
>>> def print_stuff(x,y):
... it = iter(range(1, y + 1))
... chunk = list(itertools.islice(it,X))
... while chunk:
... print(*chunk)
... chunk = list(itertools.islice(it,X))
...
>>> print_stuff(2,4)
1 2
3 4
>>>
And here:
>>> print_stuff(2,10)
1 2
3 4
5 6
7 8
9 10
>>>
I split user input into two string then convert them into int and comapre if y greater than x by 2 because this is minimum for drawing your sequence
Then i make a list from 1 to y
And iterate over it 2 element for each iteration printing them
x,y=input().split()
if int(y)>int(x)+2:
s=range(1,int(y)+1)
for i in range(0,len(s),2):
print(' '.join(str(d) for d in s[i:i+2]))
result:
1 2
3 4
5 6
7 8
9 10

Reverse loop inside a loop with same list

num = list(str(1234567))
for n1 in num:
print(n1)
for n2 in reversed(num):
print('\t', n2)
On each iteration, it prints the first digit from the first loop and all 7 from the reverse loop. How can I print not all digits but only the last (i.e first) digit from reverse loop?
Thanks
Simplest way is to just zip the forward and reverse lists together:
for n1, n2 in zip(num, reversed(num)):
print(n1, '\t', n2)
Here's a feeble attempt. Is this the kind of thing you're looking for?
for idx,i in enumerate(x):
print(i,"\t",x[-(idx+1)])
Do you mean like this?
num = list(str(1234567))
for i in range(len(num)):
print(num[i], '\t', num[-(i+1)])
Output is:
1 7
2 6
3 5
4 4
5 3
6 2
7 1
Nothing to do with Python, but here it is in Haskell :)
myDie = [1,2,3,4,5,6]
sevens = [ (x,y) | x <- myDie, y <- myDie, x+y == 7]
You should make a second list:
>>> num_rev = num[:]
>>> num_rev.reverse()
>>> num_rev
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
Then do something like:
>>> for n1,n2 in zip(num,num_rev):
... print(n1, n2)
...
0 9
1 8
2 7
3 6
4 5
5 4
6 3
7 2
8 1
9 0

Categories