Like a previous problem I had earlier, I am trying to create a breadth-first search algorithm that takes a graph and outputs the vertex visit order. It takes an adjacency matrix (representing the graph) as its input and here is what I have so far.
import sys
import Queue
# Input has to be adjacency matrix or list
graphAL2 = {0 : [1,2,3],
1 : [0,3,4],
2 : [0,4,5],
3 : [0,1,5],
4 : [1,2],
5 : [2,3] }
# NEED TO FIX:
# - Final graphAL2v print is only displaying key values as 1, not iterating
# through graph and visiting each vertex
def main():
count = 0
graphAL2v = {}
for key, value in graphAL2.items():
graphAL2v[key] = 0
print(graphAL2v)
for key in graphAL2v: # each vertex v in V
if graphAL2v[key] == 0: # is marked with 0
bfs(key, count, graphAL2, graphAL2v)
print(graphAL2v)
def bfs(v, count, graphal, graphv):
count = count + 1
print('Visiting', v)
# Mark v with count and initialize queue with v
graphv[v] = count
visited = Queue.Queue()
while not visited.empty(): #queue not empty:
print('queue is not empty')
for element in graphal[v]: # each vertex w in V adjacent to front vertex
if element == 0:
count = count + 1
# mark w with count
graphal[v] = count
visited.put()
visited.get()
if __name__ == '__main__':
sys.exit(main())
The problem that I am running into is that my output
{0: 0, 1: 0, 2: 0, 3: 0, 4: 0, 5: 0}
('Visiting', 0)
('Visiting', 1)
('Visiting', 2)
('Visiting', 3)
('Visiting', 4)
('Visiting', 5)
{0: 1, 1: 1, 2: 1, 3: 1, 4: 1, 5: 1}
displays the visit order as 1 for all vertices in the list when it should be displaying the visit order as a different number for each vertex as it traverses the "graph." I believe that this error is stemming from within the while loop of the bfs() function. Any suggestions for trying to fix the code so I can achieve the desired output? I'm also not that familiar with queues in Python so any help is appreciated.
There are lots of issues in your code -
First of all, you are never putting anything in the Queue that you create, so its always empty, you need to put the v inside the queue before the while loop , that is the starting point.
Secondly, in the for loop, you are checking element == 0 , which is wrong, you need to check if graphv[element] == 0 , that is whether the element has been already visited or not.
Thirdly, in the for loop, you need to set graphv[element] = count , that signifies that you vivisted element .
You are not putting anything inside the queue with - visited.put() , you need to pass the element to put inside the Queue as parameter.
When getting back the element from the Queue, you need to assign it back to v, otherwise v would never change, v signifies the current element being iterated.
Example code -
import sys
import Queue
# Input has to be adjacency matrix or list
graphAL2 = {0 : [1,2,3],
1 : [0,3,4],
2 : [0,4,5],
3 : [0,1,5],
4 : [1,2],
5 : [2,3] }
# NEED TO FIX:
# - Final graphAL2v print is only displaying key values as 1, not iterating
# through graph and visiting each vertex
def main():
count = 0
graphAL2v = {}
for key, value in graphAL2.items():
graphAL2v[key] = 0
print(graphAL2v)
for key in graphAL2v: # each vertex v in V
if graphAL2v[key] == 0: # is marked with 0
bfs(key, count, graphAL2, graphAL2v)
print(graphAL2v)
def bfs(v, count, graphal, graphv):
count = count + 1
print('Visiting', v)
# Mark v with count and initialize queue with v
graphv[v] = count
visited = Queue.Queue()
visited.put(v)
while not visited.empty(): #queue not empty:
print('queue is not empty')
for element in graphal[v]: # each vertex w in V adjacent to front vertex
if graphv[element] == 0:
count = count + 1
# mark w with count
graphv[element] = count
visited.put(element)
v = visited.get()
return count
if __name__ == '__main__':
sys.exit(main())
Demo (after above changes) -
{0: 0, 1: 0, 2: 0, 3: 0, 4: 0, 5: 0}
Visiting 0
queue is not empty
queue is not empty
queue is not empty
queue is not empty
queue is not empty
queue is not empty
{0: 1, 1: 2, 2: 3, 3: 4, 4: 5, 5: 6}
You can use one of the graph libraries that provide search algorithms, such as NetworkX:
from networkx import *
graphAL2 = {0 : [1,2,3],
1 : [0,3,4],
2 : [0,4,5],
3 : [0,1,5],
4 : [1,2],
5 : [2,3] }
g = Graph()
# create graph
for node in graphAL2:
g.add_node(node)
for target_node in graphAL2[node]:
g.add_edge(node, target_node)
print bfs_successors(g, 0)
This will get the successors of each node, exporting the search order from that should be a piece of cake.
Output:
{0: [1, 2, 3], 1: [4], 2: [5]}
Related
I want to optimise my model that finds the highest possible overlap (assignment) of items (probes) against a sequence (sequence). I have the starting and end positions of all items and can thus build up my model as follows:
import pyomo
import pyomo.environ as pe
import pyomo.opt as po
sequence = [0, 1, 2, 3, 4, 5]
probes = ["a", "b", "c"]
probe_starts = {"a": 0, "b": 2, "c": 3}
probe_ends = {"a": 2, "b": 4, "c": 5}
# Model definition
model = pe.ConcreteModel()
model.sequence = pe.Set(initialize=sequence)
model.probes = pe.Set(initialize=probes)
model.starts = pe.Param(model.probes, initialize=probe_starts)
model.ends = pe.Param(model.probes, initialize=probe_ends)
model.assignment = pe.Var(model.sequence, model.probes, domain=pe.Binary)
# Objective
expr = sum([model.assignment[j, i] for j in model.sequence for i in model.probes])
model.objective = pe.Objective(expr=expr, sense=pe.maximize)
I now have the following three constraints:
Items can only bind one at a time (no overlapping items)
Because items have a start and end position they are limited in only getting assigned after their respective starts and ends
If items are assigned, they have to bind in their entirety spanning from their start to their end
# One probe per sequence position
model.one_probe_bound = pe.ConstraintList()
for s in model.sequence:
model.one_probe_bound.add(sum(model.assignment[s, p] for p in model.probes) <= 1)
# No assignment before/after start/end
model.define_length = pe.ConstraintList()
for s in model.sequence:
for p in model.probes:
if s < model.starts[p]:
model.define_length.add(model.assignment[s, p] == 0)
if s > model.ends[p]:
model.define_length.add(model.assignment[s, p] == 0)
Both of the constraints above work without issue but I can't find a way to input the logical or from my third condition. I tried to use the disjunction as described in this stackoverflow answer:
# Only allow full assignment or none
def disjunct_rule(b, p, i):
m = b.model()
expr = sum(m.assignment[s, p] for s in m.sequence)
if i:
return expr == m.ends[p] - m.starts[p]
else:
return expr == 0
def disjunction_rule(m, p):
return [m.disjunct[p, i] for i in [True, False]]
def xfrm(m):
pe.TransformationFactory("gdp.bigm").apply_to(m)
model.disjunct = pyomo.gdp.Disjunct(model.probes, [True, False], rule=disjunct_rule)
model.disjunction = pyomo.gdp.Disjunction(model.probes, rule=disjunction_rule)
model.xfrm = pe.BuildAction(rule=xfrm)
Looking at the matrix representation of model.assignment with sequence along the columns and probes along the rows, I get the following:
array([[1, 1, 1, 0, 0, 0],
[0, 0, 0, 1, 1, 0],
[0, 0, 0, 0, 0, 1]])
As can be seen above, I get assignments that aren't spanning the entire length of the item (e.g. c / 3rd item is only assigned at the last position whereas it should have to bind at the two previous ones too. The only valid solution I can see in this toy example is the following:
array([[1, 1, 1, 0, 0, 0],
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 1, 1, 1]])
Where items a and c are selected in their entirety and b isn't selected at all. This way we have all constraints matched. The solver I used was glpk. Thanks in advance for any suggestions.
Here is a cut at this....
The other way I mentioned would be to introduce another binary variable for each point in sequence and control that (somehow) via the assignment of probes. The method below should be much more efficient than that.
Try this out with a larger dataset... The current only has 1 feasible solution. Also, I assumed that better solutions would use fewer probes, so I re-wrote the objective to that.
The basis of this solution is the constraint that you must connect to the start once (constraint 1) and the end (constraint 2) once. And any intermediate connections must be consistent (constraint 3).
I used some sub-setting in a few spots where needed.
Code:
# model to make contiguous connections across a sequence
# using as few connections (probes) as possible
import pyomo
import pyomo.environ as pe
sequence = [0, 1, 2, 3, 4, 5]
probes = ["a", "b", "c"]
probe_starts = {"a": 0, "b": 2, "c": 3}
probe_ends = {"a": 2, "b": 4, "c": 5}
# Model definition
model = pe.ConcreteModel()
model.sequence = pe.Set(initialize=sequence)
model.probes = pe.Set(initialize=probes)
model.starts = pe.Param(model.probes, initialize=probe_starts)
model.ends = pe.Param(model.probes, initialize=probe_ends)
model.assign = pe.Var(model.probes, domain=pe.Binary) # 1 if probe p is used...
# Objective
obj = sum(model.assign[p] for p in model.probes) # use as few as possible...?
model.objective = pe.Objective(expr=obj, sense=pe.minimize)
# Constraints
# must connect start once
model.C1 = pe.Constraint(expr=sum(model.assign[p] for p in model.probes
if model.starts[p] == sequence[0]) == 1)
# must connect end once
model.C2 = pe.Constraint(expr=sum(model.assign[p] for p in model.probes
if model.ends[p] == sequence[-1]) == 1)
# must connect any intermediate connections...
# if probe p1 is selected, must select an eligible p2 follow on
def connect(model, p1):
# create subset on the fly of legal follow-on connections
# assumption here that sequence is a sequential list of ints by using the "+1"
p2s = [p for p in model.probes if model.starts[p] == model.ends[p1] + 1]
if not p2s:
return pe.Constraint.Skip
return sum(model.assign[p2] for p2 in p2s) == model.assign[p1]
non_completing_probes = [p for p in model.probes if model.ends[p] != sequence[-1]]
model.C3 = pe.Constraint(non_completing_probes, rule=connect)
solver = pe.SolverFactory('glpk')
result = solver.solve(model)
print(result)
model.display()
Yields:
Problem:
- Name: unknown
Lower bound: 2.0
Upper bound: 2.0
Number of objectives: 1
Number of constraints: 4
Number of variables: 4
Number of nonzeros: 5
Sense: minimize
Solver:
- Status: ok
Termination condition: optimal
Statistics:
Branch and bound:
Number of bounded subproblems: 0
Number of created subproblems: 0
Error rc: 0
Time: 0.006771087646484375
Solution:
- number of solutions: 0
number of solutions displayed: 0
Model unknown
Variables:
assign : Size=3, Index=probes
Key : Lower : Value : Upper : Fixed : Stale : Domain
a : 0 : 1.0 : 1 : False : False : Binary
b : 0 : 0.0 : 1 : False : False : Binary
c : 0 : 1.0 : 1 : False : False : Binary
Objectives:
objective : Size=1, Index=None, Active=True
Key : Active : Value
None : True : 2.0
Constraints:
C1 : Size=1
Key : Lower : Body : Upper
None : 1.0 : 1.0 : 1.0
C2 : Size=1
Key : Lower : Body : Upper
None : 1.0 : 1.0 : 1.0
C3 : Size=1
Key : Lower : Body : Upper
a : 0.0 : 0.0 : 0.0
I am trying to do a multiple totaling statements but it keeps saying index out of range.
Here is the section of code:
for m in range(len(mo)):
for o in range(len(mag)):
if mag[o] == 0 and mo[m] ==1 :
countfujita[m] = countfujita[m] + 1
and I am trying to get the totals into list a list such as this:
countfujita = [0,0,0,0,0,0]
I suspect this is because you are looping over mag for every item in mo when this is not what you want. Let me know if the following fixes your issue:
for m in range(len(mo)):
if mag[m] == 0 and mo[m] ==1 :
countfujita[m] = countfujita[m] + 1
(this assumes that len(mag) = len(mo))
In order for your code to run successfully you need to ensure that countfujita is at least as long as the mo list.
The following would be a robust approach:
mo = [1, 0, 3]
mag = [3, 0, 1, 11]
# construct a list of the same length as *mo* and fill with zeroes
countfujita = [0] * len(mo)
for m in range(len(mo)):
for o in range(len(mag)):
if mag[o] == 0 and mo[m] == 1:
countfujita[m] += 1
print(countfujita)
Output:
[1, 0, 0]
Indexing multiple lists inside nested loops is error-prone. In general we want to leverage the python built-in modules as much as possible.
For example (starting from the example defined by Lancelot du Lac) we can use itertools.product to generate all combinations of mo and mag. enumerate gives us the index corresponding to the element of mo:
from itertools import product
for (imo, xmo), xmag in product(enumerate(mo), mag):
if (xmo, xmag) == (1, 0):
countfujita[imo] += 1
To push this even further, we can combine this with Counter to first generate a list of all mo indices and then count. This results in a counter object Counter({0: 1}) object, similar to a dict, which might or might not be appropriate depending on what you do with countfujita later on:
from itertools import product
from collections import Counter
Counter([imo for (imo, xmo), xmag
in product(enumerate(mo), mag)
if (xmo, xmag) == (1, 0)])
# Counter({0: 1})
I am trying to write a program which finds duplicate coordinates (x, y, z) in a 3D array. The script should mark one or multiple duplicate points with a given tolerance - one point could have more than one duplicate. I found lots of different approaches which among others use sorting approaches.
To try the code I created the following test data set:
21.9799629872016 57.4044376777929 0
22.7807110172432 57.6921361034533 0
28.660840151287 61.5676757599822 0
28.6608401512 61.56767575998 0
30.6654296288019 56.2221038199424 0
20.3752036442253 49.1392209993897 0
32.8036584048178 43.927288357851 0
35.8105426210901 51.9456462679106 0
40.8888359641279 58.6944308422108 0
40.88883596412 70.6944308422108 0
41.0892949118794 58.1598736482068 0
39.6860822776189 64.775018924006 0
39.1515250836149 64.8418385732565 0
8.21402748063493 63.5054455882466 0
8.2140275006 63.5074455882 0
8.21404548063493 63.5064455882466 0
8.2143214806 63.5084455882 0
The code I came up with is:
# given tolerance
tol = 0.01
# initialize empty list for the found duplicates
duplicates = []
# loop over all nodes
for i in range(0,len(nodes)):
# current node
curr_node = nodes[i]
# create difference vector
diff = nodes - curr_node
# get all duplicate indices (the node itself is found as well)
condition = np.where((abs(diff[:,0])<tol) & (abs(diff[:,1])<tol) & (abs(diff[:,2])<tol))
# check if more than one entry is present. If larger than 1, duplicate points exist
if len(condition[0]) > 1:
# loop over all found duplicate points
for j in range(0,len(condition[0])):
# add duplicate if not already marked as duplicate
if j>0 and condition[0][j] not in duplicates:
duplicates.append(condition[0][j] )
This code returns what I am expecting:
duplicates = [3, 14, 15, 16]
However, the code is very slow. For 300,000 points it takes about 10 minutes. I am wondering if there is any faster way to implement this.
You can place points in a grid of tolerance-sized cubes. Then, for each point, you only need to check the points from the same cube + 26 adjacent ones instead of all other points.
# compute the grid
for p in points:
cube = (
int(p[0] / tolerance),
int(p[1] / tolerance),
int(p[2] / tolerance))
grid[cube].append(p)
# check
for p in points:
cube = as above
for adj in adjacent_cubes(cube)
for p2 in grid[adj]
check_distance(p, p2)
You could sort the nodes upfront, to reduce the amount of loops needed:
import timeit
import random
nodes = [
[21.9799629872016, 57.4044376777929, 0],
[22.7807110172432, 57.6921361034533, 0],
[28.660840151287, 61.5676757599822, 0], [28.6608401512, 61.56767575998, 0],
[30.6654296288019, 56.2221038199424, 0],
[20.3752036442253, 49.1392209993897, 0],
[32.8036584048178, 43.927288357851, 0],
[35.8105426210901, 51.9456462679106, 0],
[40.8888359641279, 58.6944308422108, 0],
[40.88883596412, 70.6944308422108, 0],
[41.0892949118794, 58.1598736482068, 0],
[39.6860822776189, 64.775018924006, 0],
[39.1515250836149, 64.8418385732565, 0],
[8.21402748063493, 63.5054455882466, 0], [8.2140275006, 63.5074455882, 0],
[8.21404548063493, 63.5064455882466, 0], [8.2143214806, 63.5084455882, 0]
]
duplicates = [3, 14, 15, 16]
assertList = [n for i, n in enumerate(nodes) if i in duplicates]
def new(nodes, tol=0.01):
print(f"Searching duplicates in {len(nodes)} nodes")
coordinateLen = range(len(nodes[0]))
nodes.sort()
last = nodes[0]
duplicates = []
for i, node in enumerate(nodes[1:]):
if not all(0 <= node[idx] - last[idx] < tol for idx in coordinateLen):
last = node
else:
duplicates.append(node)
print(f"Found: {len(duplicates)} duplicates")
return duplicates
# generate random numbers!
randomNodes = [
[random.uniform(0, 100),
random.uniform(0, 100),
random.uniform(0, 1)] for _ in range(300000)
]
# make sure there are at least the same 4 duplicates!
randomNodes += nodes
for i, lst in enumerate((nodes, randomNodes)):
for func in ("new", ):
t1 = timeit.Timer(f"{func}({lst})", f"from __main__ import {func}")
# verify values of found duplicates are [3, 14, 15, 16] !!
if i == 0:
print(all(x for x in new(nodes) if x in assertList))
print(f"{func} took: {t1.timeit(number=10)} seconds")
print("")
Out:
Searching duplicates in 17 nodes
Found: 4 duplicates
True
....
new took: 0.00034904800000001845 seconds
Searching duplicates in 300017 nodes
Found: 4 duplicates
...
new took: 14.316181525000001 seconds
I have three lists, each one with several possible values.
probs = ([0.1,0.1,0.2], \
[0.7,0.9], \
[0.5,0.4,0.1])
I want to test all possible combinations of choosing one element from each list. So, 3*2*3=18 possible combinations in this example. In the end, I want to choose the most favourable combinations according to some criteria. This is:
[<index in row 0> , <index in row 1> , <index in row 2> , <criteria value>]
I can accomplish my task by using three nested for loops (which I did). However, in the real application of this code, I will have a variable number of lists. Because of that, it seems the solution would be using a recursive function with a for loop inside it (which I did as well). The code:
# three rows. Test all combinations of one element from each row
# This is [value form row0, value from row1, value from row2]
# So: 3*2*3 = 18 possible combinations
probs = ([0.1,0.1,0.2], \
[0.7,0.9], \
[0.5,0.4,0.1])
meu = [] # The list that will store the best combinations in the recursion
#######################################################
def main():
choice = [] #the list that will store the best comb in the nested for
# accomplish by nested for loops
for n0 in range(len(probs[0])):
for n1 in range(len(probs[1])):
for n2 in range(len(probs[2])):
w = probs[0][n0] * probs[1][n1] * probs[2][n2]
cmb = [n0,n1,n2,w]
if len(choice) == 0:
choice.append(cmb)
elif len(choice) < 5:
for i in range(len(choice)+1):
if i == len(choice):
choice.append(cmb)
break
if w < choice[i][3]:
choice.insert(i,cmb)
break
else:
for i in range(len(choice)):
if w < choice[i][3]:
choice.insert(i,cmb)
del choice[-1]
break
# using recursive function
combinations(0,[])
#both results
print('By loops:')
print(choice)
print('By recursion:')
print(meu)
#######################################################
def combinations(step,cmb):
# Why does 'meu' needs to be global
if step < len(probs):
for i in range(len(probs[step])):
cmb = cmb[0:step] # I guess this is the same problem I dont understand recursion
# But, unlike 'meu', here I could use this workaround
cmb.append(i)
combinations(step+1,cmb)
else:
w = 1
for n in range(len(cmb)):
w *= probs[n][cmb[n]]
cmb.append(w)
if len(meu) == 0:
meu.append(cmb)
elif len(meu) < 5:
for i in range(len(meu)+1):
if i == len(meu):
meu.append(cmb)
break
if w < meu[i][-1]:
meu.insert(i,cmb)
break
else:
for i in range(len(meu)):
if w < meu[i][-1]:
meu.insert(i,cmb)
del meu[-1]
break
return
######################################################
main()
It outputs, as I wanted:
By loops:
[[0, 0, 2, 0.006999999999999999], [1, 0, 2, 0.006999999999999999], [0, 1, 2, 0.009000000000000001], [1, 1, 2, 0.009000000000000001], [2, 0, 2, 0.013999999999999999]]
By recursion:
[[0, 0, 2, 0.006999999999999999], [1, 0, 2, 0.006999999999999999], [0, 1, 2, 0.009000000000000001], [1, 1, 2, 0.009000000000000001], [2, 0, 2, 0.013999999999999999]]
Initially, I wanted to use the 'meu' list as internal of the function, because, I thought, it would be better to avoid global variables (perhaps not... I'm a newbie). The problem was I could not come up with a code that would pass both 'meu' and 'cmb' between depths to give the same effect of the nested loops.
How could I implement a recursive function with internal 'meu' instead of being a global list? What am I missing from recursion concept? Thanks.
++++++++++++++++++++++++++++++++++
Example of a failed function:
def combinations(choice,step,cmb):
if step < len(probs):
for i in range(len(probs[step])):
cmb = cmb[0:step] #workaroud for cmb
cmb.append(i)
choice = combinations(choice,step+1,cmb)
else:
w = 1
for n in range(len(cmb)):
w *= probs[n][cmb[n]]
cmb.append(w)
if len(choice) == 0:
choice.append(cmb)
elif len(choice) < 5:
for i in range(len(choice)+1):
if i == len(choice):
choice.append(cmb)
break
if w < choice[i][-1]:
choice.insert(i,cmb)
break
else:
for i in range(len(choice)):
if w < choice[i][-1]:
choice.insert(i,cmb)
del choice[-1]
break
return choice
Called by:
choice = combinations([],0,[])
Don't reinvent the wheel (recursively or not): use the included batteries. The problem you are trying to solve is extremely common and so a solution is included in Python's standard library.
What you want—every combination of every value from some number of lists—is called the Cartesian product of those lists. itertools.product exists to generate those for you.
import itertools
probs = ([0.1, 0.1, 0.2],
[0.7, 0.9],
[0.5, 0.4, 0.1])
for prob in itertools.product(*probs):
print prob
# prob is a tuple containing one combination of the variables
# from each of the input lists, do with it what you will
If you want to know what index each item comes from, the easiest way is to just pass the indices to product() rather than the values. You can easily get that using range().
for indices in itertools.product(*(range(len(p)) for p in probs)):
# get the values corresponding to the indices
prob = [probs[x][indices[x]] for x in range(len(probs))]
print indices, prob
Or you could use enumerate() -- this way, each item in the product is a tuple containing its index and its values (not two separate lists the way you get them in the above method):
for item in itertools.product(*(enumerate(p) for p in probs)):
print item
I am troubled with the following problem.
I have a list with symmetric items, e.g., k = [-1,1,-2,2,-3,3]. Depending of the number of items (Ni), I would like to create another list, e, using it in the following way: each item of list k can generate in the max. 2 items in the list e, but there is a “order” of filling of the list e.
For example, if we have Ni=4 then there is 4 items in e. I will fill e according the following way: each item of k can ‘support’ 2 values. As the second value is filled only after its symmetric and corresponding value be filled, as we have 4 in this example, the 1st item of e is -1, 2nd would be 1, the 3rd, -1 again, and finally the 4th would be 1.
If I have Ni=5, the fifth element would be -2, 6th would be 2, 7th , -2 and the eighth 2. The examples below illustrate this.
My initial code is:
k=[-1,1,-2,2,-3,3]
Ni=input("Ni: ")
e=[]
temp = 1
for i in k:
e.append(i)
temp+=1
if temp>Ni:
break
---
And then I get
e = [-1,1,-2,2,-3]
and this result of course is not exactly what I want. The examples below illustrate better what I mean.
Example 0: Ni = 1
The list would be:
e=[-1]
Example 1: Ni = 2
The list ‘e’ would be:
e=[-1,1]
Example 2: Ni = 3
e=[-1,1,-1]
Ex. 3: Ni = 4
e=[-1,1,-1,1]
Ex. 4: Ni = 5
e=[-1,1,-1,1,-2]
Ex. 5: Ni = 6
e=[-1,1,-1,1,-2,2]
Ex. 6: Ni = 7
e=[-1,1,-1,1,-2,2,-2]
Ex.7: Ni = 8
e=[-1,1,-1,1,-2,2,-2,2]
Thanks in advance for any help, ideas, suggestions, etc!
Ni = int(input("Select number of items: "))
k = [-1,1,-2,2,-3,3]
if Ni <= len(k) + 2:
if Ni <= 2:
e = k[0:Ni]
else:
e = [x for x in sorted(k[0:2]*2) + k[2:len(k)]][0:Ni]
import itertools
def fill(k, Ni):
return sorted(list(itertools.islice(itertools.chain.from_iterable(zip(itertools.chain.from_iterable((k[i], k[i+1])*2 for i in range(0,len(k),2)), )), 0, Ni)), key=lambda i:(abs(i), i))
Or, a slightly more readable version:
def fill(k, Ni):
return sorted(list(
itertools.islice(
itertools.chain.from_iterable(
zip(
itertools.chain.from_iterable((k[i], k[i+1])*2 for i in range(0,len(k),2)),
)
),
0, Ni
)
), key=lambda i:(abs(i), i))