Shuffling an array without built in functions? - python

Trying to shuffle an array without using built in functions.
def shuffle2():
lis = [5,6,7,10,11,12]
print (lis)
x = random.randint (0,len (lis)-1)
y = random.randint (0,len (lis)-1)
z = lis[x], lis[y] = lis[y], lis[x]#flips the elements around
shuffle2()
So far it switches two elements around once, however I need it to shuffle the whole array rather than just two positions.
output:
[5, 6, 7, 10, 11, 12]
[11, 6, 7, 10, 5, 12]
Any ideas?

Here's one way:
import bisect
import random
def shuffle(lis):
newlis = []
temp = []
for i in lis:
x = random.random()
#put x into temp and put i into the same place in newlis
n = bisect.bisect(temp,x)
temp.insert(n,x)
newlis.insert(n,i)
return newlis
lis=[5,6,7,10,11,12]
>>> shuffle(lis)
[6, 12, 10, 11, 7, 5]
The method essentially generate random numbers and puts them into a sorted list (temp). The items is lis are put into a newlis in the same way, which has the effect of sorting it.
You, of course, can write your own bisect, or use a more trivial search function quite easily...

import random
def shuffle2():
i = 1
lis = [5,6,7,10,11,12]
print (lis)
while i < len(lis):
x = random.randint (0,len (lis)-1)
y = random.randint (0,len (lis)-1)
z = lis[x], lis[y] = lis[y], lis[x]
i = i + 1
#flips the elements around
print (lis)

Related

Double the size of a list of random integers, then sort and print it

Here, I have a list of random integers:
import random
list = [random.randint(0, 30) for x in range(6)]
pass
I want to double the size of this list, sort it, and print it. Here's what I've tried:
def list_doubled(list):
doubled = []
i = 0
while i <= len(list):
for item in list:
doubled.append(list[i])
doubled.append(list[i])
i += 1
print(doubled)
list_doubled(list)
This code is meant to only double the size of the list. When I run the program, I get "IndexError: list index out of range" with emphasis on lines 11 and 16.
In Python you can double a list by just multiplying it by 2.
>>> import random
>>> nums = [random.randint(0, 30) for _ in range(6)]
>>> print(nums)
[5, 30, 28, 11, 19, 17]
>>> print(sorted(nums * 2))
[5, 5, 11, 11, 17, 17, 19, 19, 28, 28, 30, 30]
Creating the list:
import random
numbers = [random.randint(0, 30) for x in range(6)]
Use extend to add the list to itself, in order to double it:
numbers.extend(numbers) # or numbers += numbers
Or add as many random numbers as there are items in the list, if you need the additional numbers to also be random:
numbers.extend(random.randint(0, 30) for _ in range(len(numbers)))
use sort to sort it:
numbers.sort()
Use print to print it:
print(numbers)
Change your <= to < in your while loop line while i <= len(list):. You also might want to look at the comments, they have good advise on additional potential bugs you might encounter.

Trying to figure out how to iterate a list twice in a One function

I am trying to figure out how to iterate over fn to modify the list again with only even numbers, AFTER appending. The desire result is
[2,4,6,8,10,12,14,16,18,20]
is this possible? I do not want to add fn + sn or lst + lst2. I want to continue over this specific function.
lst = [1,2,3,4,5,6,7,8,9,10]
lst2 =[11,12,13,14,15,16,17,18,19,20]
def even(fn,sn):
for i in sn:
if i %2 ==0:
fn.append(i)
even(lst,lst2)
print(lst) # output [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 16, 18, 20]
I know there are more efficient and pythonic ways like this example:
example 1:
def even2(fn,sn):
fn[:] = [x for x in fn + sn if x % 2 == 0]
example 2:
def even(fn, sn):
fn.extend(sn)
fn[:] = [x for x in fn if x % 2 == 0]
def even(fn,sn):
for i in sn:
if i %2 ==0:
fn.append(i)
fn = list(filter(lambda x: x %2 == 0, fn))
the last code block is what i tried and failed with
You don't need to rebuild the entire list; just extend it with the desired extra elements.
>>> lst = [1,2,3,4,5,6,7,8,9,10]
>>> lst2 =[11,12,13,14,15,16,17,18,19,20]
>>> def even(fn, sn):
... fn.extend(i for i in sn if i % 2 == 0)
...
>>> even(lst, lst2)
>>> lst
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 16, 18, 20]
In the above expression i for i... is a generator expression that allows extend to iterate over all of the i elements without storing them in a separate list first.
You could do it in two steps:
remove odd numbers from fn
extend fn with the even numbers in sn
This function does it without creating any temporary copies or sublists:
lst = [1,2,3,4,5,6,7,8,9,10]
lst2 =[11,12,13,14,15,16,17,18,19,20]
def even(fn,sn):
for i,n in enumerate(reversed(fn),1-len(fn)): # delete odds backwards
if n%2: del fn[-i] # so indexes stay stable
fn.extend(n for n in sn if n%2==0) # extend with sn's evens
even(lst,lst2)
print(lst)
# [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
Note that you could extend fn first and then remove the odd numbers, but that would be inefficient:
def even(fn,sn):
fn.extend(sn) # add all sn values
for i,n in enumerate(reversed(fn),1-len(fn)): # delete odds backwards
if n%2: del fn[-i] # so indexes stay stable
A more efficient way would be to iterate through the two lists and assign the result to fn[:]. Doing this starting with fn will ensure that there is no conflict between the iterators and the assignment process:
def even(fn,sn):
fn[:] = (n for L in (fn,sn) for n in L if n%2==0)
The nested comprehension loops allow iterating through the two lists without actually concatenating them so there is no temporary copies or sublists in that solution either.
Does this meet your requirements? Pulling lst into the even() func as a global.
lst = [1,2,3,4,5,6,7,8,9,10]
lst2 =[11,12,13,14,15,16,17,18,19,20]
def even(fn, sn):
global lst
lst = [x for x in (fn + sn) if x % 2 == 0]
even(lst,lst2)
print(lst)

Modify function to accept tables as well (Python 3)

I've designed a simple function that looks at an inputted list of numbers, identifies the minimum and maximum values, then substitutes both of them for the midpoint value between them, the function is here:
def mainfunction(list_of_numbers):
smallest_number = list_of_numbers[0]
for a in list_of_numbers:
if a < smallest_number:
smallest_number = a
largest_number = list_of_numbers[0]
for b in list_of_numbers:
if b > largest_number:
largest_number = b
midpoint = (smallest_number + largest_number)/2
final_list = [x if (x != largest_number and x != smallest_number) else midpoint for x in list_of_numbers]
return final_list
print(mainfunction([10, 7, 14, 3, -200, 8, 1, -12, 250]))
Unfortunately, I can't get the function to work on TABLES of numbers, is there an easy way to convert a table of numbers into a list? Any info would be appreciated, cheers!
You can use itertools.chain
from itertools import chain
a = [[3,4], [15,16], [19,20]]
res = list(chain.from_iterable(a))
print(res)
Output:
[3, 4, 15, 16, 19, 20]
with list comprehension
res = [x for lst in a for x in lst]

what is the fastest way to increment a slice of a list?

I have a list:
lst = [ 1,2,3,4,5,6,7,8]
I want to increment all numbers above index 4.
for i in range(4,len(lst)):
lst[i]+=2
Since this operation needs to be done many time, I want to do it the most efficient way possible.
How can I do this fast.
Use Numpy for fast array manipulations, check the example below:
import numpy as np
lst = np.array([1,2,3,4,5,6,7,8])
# add 2 at all indices from 4 till the end of the array
lst[4:] += 2
print(lst)
# array([ 1, 2, 3, 4, 7, 8, 9, 10])
If you are updating large ranges of a large list many times, use a more suitable data structure so that the updates don't take O(n) time each.
One such data structure is a segment tree, where each list element corresponds to a leaf node in a tree; the true value of the list element can be represented as the sum of the values on the path between the leaf node and the root node. This way, adding a number to a single internal node is effectively like adding it to all of the list elements represented by that subtree.
The data structure supports get/set operations by index in O(log n) time, and add-in-range operations also in O(log n) time. The solution below uses a binary tree, implemented using a list of length <= 2n.
class RangeAddList:
def __init__(self, vals):
# list length
self._n = len(vals)
# smallest power of 2 >= list length
self._m = 1 << (self._n - 1).bit_length()
# list representing binary tree; leaf nodes offset by _m
self._vals = [0]*self._m + vals
def __repr__(self):
return '{}({!r})'.format(self.__class__.__name__, list(self))
def __len__(self):
return self._n
def __iter__(self):
for i in range(self._n):
yield self[i]
def __getitem__(self, i):
if i not in range(self._n):
raise IndexError()
# add up values from leaf to root node
t = 0
i += self._m
while i > 0:
t += self._vals[i]
i >>= 1
return t + self._vals[0]
def __setitem__(self, i, x):
# add difference (new value - old value)
self._vals[self._m + i] += x - self[i]
def add_in_range(self, i, j, x):
if i not in range(self._n + 1) or j not in range(self._n + 1):
raise IndexError()
# add at internal nodes spanning range(i, j)
i += self._m
j += self._m
while i < j:
if i & 1:
self._vals[i] += x
i += 1
if j & 1:
j -= 1
self._vals[j] += x
i >>= 1
j >>= 1
Example:
>>> r = RangeAddList([0] * 10)
>>> r.add_in_range(0, 4, 10)
>>> r.add_in_range(6, 9, 20)
>>> r.add_in_range(3, 7, 100)
>>> r
RangeAddList([10, 10, 10, 110, 100, 100, 120, 20, 20, 0])
It turns out that NumPy is so well-optimized, you need to go up to lists of length 50,000 or so before the segment tree catches up. The segment tree is still only about twice as fast as NumPy's O(n) range updates for lists of length 100,000 on my machine. You may want to benchmark with your own data to be sure.
This is a fast way of doing it:
lst1 = [1, 2, 3, 4, 5, 6, 7, 8]
new_list = [*lst[:4], *[x+2 for x in lst1[4:]]]
# or even better
new_list[4:] = [x+2 for x in lst1[4:]]
In terms of speed, numpy isn't faster for lists this small:
import timeit
import numpy as np
lst1 = [1, 2, 3, 4, 5, 6, 7, 8]
npa = np.array(lst)
def numpy_it():
global npa
npa[4:] += 2
def python_it():
global lst1
lst1 = [*lst1[:4], *[x+2 for x in lst1[4:]]]
print(timeit.timeit(numpy_it))
print(timeit.timeit(python_it))
For me gets:
1.7008036
0.6737076000000002
But for anything serious numpy beats generating a new list for the slice that needs replacing, which beats regenerating the entire list (which beats in-place replacement with a loop like in your example):
import timeit
import numpy as np
lst1 = list(range(0, 10000))
npa = np.array(lst1)
lst2 = list(range(0, 10000))
lst3 = list(range(0, 10000))
def numpy_it():
global npa
npa[4:] += 2
def python_it():
global lst1
lst1 = [*lst1[:4], *[x+2 for x in lst1[4:]]]
def python_it_slice():
global lst2
lst2[4:] = [x+2 for x in lst2[4:]]
def python_inplace():
global lst3
for i in range(4, len(lst3)):
lst3[i] = lst3[i] + 2
n = 10000
print(timeit.timeit(numpy_it, number=n))
print(timeit.timeit(python_it_slice, number=n))
print(timeit.timeit(python_it, number=n))
print(timeit.timeit(python_inplace, number=n))
Results:
0.057994199999999996
4.3747423
4.5193105000000005
9.949074000000001
Use assign to slice:
lst[4:] = [x+2 for x in lst[4:]]
Test (on my ancient ThinkPad i3-3110, Python 3.5.2):
import timeit
lst = [1, 2, 3, 4, 5, 6, 7, 8]
def python_it():
global lst
lst = [*lst[:4], *[x+2 for x in lst[4:]]]
def python_it2():
global lst
lst[4:] = [x+2 for x in lst[4:]]
print(timeit.timeit(python_it))
print(timeit.timeit(python_it2))
Prints:
1.2732834180060308
0.9285018060181756
use python builtin map function and lambda
lst = [1,2,3,4,5,6,7,8]
lst[4:] = map(lambda x:x+2, lst[4:])
print(lst)
# [1, 2, 3, 4, 7, 8, 9, 10]

Create a list of random integers and then put all numbers that are the same right beside eachother

def run():
lst=[]
for i in range(0,20):
ran = random.randint(1,10)
lst.append(ran)
return lst
So far I have created a list of random integers from 1 to 9 with 20 values, however how can I incorporate a swapping method so that values that are the same but not next to eachother will be next to eachother?
Thanks
You can build your own sorting criteria using indexes for the key argument.
import random
def run():
lst=[]
for i in range(0,20):
ran = random.randint(1,10)
lst.append(ran)
return lst
lst = run()
print(lst)
#[5, 10, 5, 1, 8, 10, 10, 6, 4, 9, 3, 9, 6, 9, 2, 9, 9, 1, 7, 8]
result = sorted(lst, key = lambda x: lst.index(x))
print(result)
#[5, 5, 10, 10, 10, 1, 1, 8, 8, 6, 6, 4, 9, 9, 9, 9, 9, 3, 2, 7]
Perhaps just sort the list:
lst = sorted(lst)
import random
#this is the function you gave with little edits, to see the changes it make
# after the process
def run():
lst=[]
for i in range(0,20):
ran = random.randint(1,10)
lst.append(ran)
print(lst)
swap(lst)
print(lst)
return lst
#this uses indexes of every element, and checks every other element of the list.
#this swap function works for lists with element made up of strings as well.
def swap(lst):
for i in range(len(lst)):
nu_m=lst[i]
x=i+1
while x<len(lst):
dump=i+1
acc=lst[i+1]
if(lst[i]==lst[x]):
lst[dump]=lst[x]
lst[x]=acc
x=x+1
x=run()
First let's create another list to keep the order of the unique numbers (like a set, but not sorted).
unsorted_set = []
for nb in lst:
if nb not in unsorted_set:
unsorted_set.append(nb)
Now that we got this list, let's create a final list that will continue that list but each number will be repeated n times, n is the occurences of the number in the first list. We will do this with lst.count()
final_list = []
for nb in unsorted_set:
for _ in range(lst.count(nb)):
final_list.append(nb)
Note that this code can be simplified a lot with List Comprehension.

Categories