Python: Confused about random.choice within join - python

Here is my code:
s = 'Hello world'
c = ['a','b','c','d','e','f']
n = ['1','2','3','4','5','6']
l = [random.choice(c),random.choice(n)]
return ''.join('%s%s' % (x, random.choice(l) if random.random() > 0.5 else '') for x in s)
This will output:
He5lloe w5o5rl5de
But what I am aiming for is something like this code would produce:
s = 'Hello world'
n = ['1','2','3','4','5','6']
return ''.join('%s%s' % (x, random.choice(n) if random.random() > 0.5 else '') for x in s)
Which is:
H4e3l3l6o wo4r3ld
It would be great if someone could also explain as to why the two are reacting differently than I would have assumed.
Sorry, I should have stated my intentions. I would like to randomly select an element from the two lists through each iteration of the for loop within join. Instead what I have is 2 elements being selected once and randomly choosing between the two elements selected.
This is what I don't want:
n = [1,2,3,4,5]
s = ['!','-','=','~','|']
l = [random.choice(n), random.choice(s)] # 1,!
# He1l!lo W!or1l!d
This is what I want:
n = [1,2,3,4,5] # 1 or 2 or 3... etc.
s = ['!','-','=','~','|'] # ! or - or =... etc.
> code to randomly select a list and a new element from that list
# He4ll-o W!or3l~d
Not sure if I have worded myself correctly, but hopefully it is understandable,

By doing l = [random.choice(c),random.choice(n)] you're limiting random.choice(l) to only 2 possible chars (one from each list c and n).
Try this instead:
from random import random, choice
s = 'Hello world'
c = ['a','b','c','d','e','f']
n = ['1','2','3','4','5','6']
L = choice([c, n]) # randomly choose either c or n
return ''.join('%s%s' % (x, choice(L) if random() > 0.5 else '') for x in s)
As an aside, assuming you want to keep the probability of an insertion at 0.5, that can also be written as:
# for each char, either append an empty string or a random char from list
return ''.join('%s%s' % (x, choice((choice(L), ""))) for x in s)
Update
Note that the above answer chooses a substitution list (c or n) and uses it for the whole process. If you want to be able to use both lists in the substitution, you can either create an intermediate list (L = c + n), or perform the list selection in-line.
# This is rather convoluted
return ''.join('%s%s' % (x, choice((choice(choice([c, n])), ""))) for x in s)
Alternatively,
e = ("", ) # tuple with a single empty element
return ''.join('%s%s' % (x, choice(choice([c, n, e, e]))) for x in s)
Choose between c, n, or empty list e (e appears twice to keep the non-empty probability at 50%. Change as required)
From the chosen list/tuple, choose a random element

Related

How to make the output return a number?

I created this code so that the program will print out all numbers within range (1000 to 10,000) if it is divisible by value k, as set below, but the ouput yields none.. what am I doing wrong?
k = 6
def pincode(k: int):
for x in range(1000,10000):
if x // k == 0:
print(x)
print(pincode(k))
what am I supposed to change to make sure that the code prints out all numbers within the range divisible by k?
There are two bugs, here for printing the function, you need to return value. If you've written print already then just call the function. If you want to print k for x%k==0 then x has multiple values. You can return multiple values by collecting x values to list. The second one is, it is x%k==0 and not x//k==0. // gives you whole number quotient and % will give you remainder. Eg, 49//7 is 7 and 49%7 is 0 and 26//7 is 3 and 26%7 is 5. Your new code:
k = 6
def pincode(k: int):
collect=[]
for x in range(1000,10000):
if x % k == 0:
collect.append(x)
return collect
print(pincode(k))
You can use a single comprehension for such a task.
k = 6
print([x for x in range(1000, 10000) if x % k == 0])
I think you should try changing the // in if x // k == 0: to % which is an operator that returns the remainder instead of the quotient.
Your function pincode(k) doesn't have a return argument, so it returns none. Append the values to a list, then add that list to the return argument.
k = 6
def pincode(k: int):
a = [] #empty list
for x in range(1000,10000):
if x % k == 0: # use % instead of //
a.append(x) # append x to list
return a #return the list
print(pincode(k))
The double forward slash in Python is known as the integer division operator. Essentially, it will divide the left by the right, and only keep the whole number component.
I would suggest to use % to find if the number is divisible.
k = 6
def pincode(k: int):
for x in range(1000,10000):
#print(f"x and k {x} and {k} res {x%k}")
if x % k == 0:
print(x)
print(pincode(k))

how to extract the max numeric substring from a string?

when given a string. how to extract the biggest numeric substring without regex?
if for example given a string: 24some555rrr444
555 will be the biggest substring
def maximum(s1)
sub=[]
max=0
for x in s1
if x.isnummeric() and x>max
sub.append(x)
max=x
return max
what to do in order this code to work?
Thank you in advance!
Replace all non digits to a space, split the resulting word based on spaces, convert each number to a int and then find the max of them
>>> s = '24some555rrr444'
>>> max(map(int, ''.join(c if c.isdigit() else ' ' for c in s).split()))
555
You could use itertools.groupby to pull out the digits in groups and find the max:
from itertools import groupby
s = "24some555rrr444"
max(int(''.join(g)) for k, g in groupby(s, key=str.isdigit) if k)
# 555
Not using regex is wierd, but ok
s = "24some555rrr444"
n = len(s)
m = 0
for i in range(n):
for len in range(i + 1, n + 1):
try:
v = int(s[i:len])
m = v if v > m else m
except:
pass
print(m)
Or if want really want to compress it to basically one line (except the convert function), you can use
s = "24some555rrr444"
n = len(s)
def convert(s):
try:
return int(s)
except:
return -1
m = max(convert(s[i:l]) for i in range(n) for l in range(i + 1, n + 1))
print(m)
In order to stay in your mindset, i propose this, it is quite close from the initial demand:
#Picked a random number+string stringchain
OriginalStringChain="123245hjkh2313k313j23b"
#Creation of the list which will contain the numbers extracted from the formerly chosen stringchain
resultstable=[]
#The b variable will contain the extracted numbers as a string chain
b=""
for i,j in zip(OriginalStringChain, range(len(OriginalStringChain))):
c= j+1
#the is.digit() fonction is your sql isnumeric equivalent in python
#this bloc of if will extract numbers one by one and concatenate them, if they are several in a row, in a string chain of numbers before adding them in the resultstable
if i.isdigit() == True:
b+=i
if j < len(OriginalStringChain)-1 and OriginalStringChain[c].isdigit() == False:
resutstable.append(int(b))
elif j== len(OriginalStringChain)-1 and OriginalStringChain[j].isdigit() == True:
resultstable.append(int(b))
else:
b=""
print(resultstable)
#At the end, you just obtain a list where every values are the numbers we extracted previously and u just use the max function
print(max(resultstable))
I hope i was clear.
Cheers

the difference in results by different data types in python

my python file like below, but I don't know why the result is difference,
the only difference is about z&k , z is int and k is str.
l = []
j = []
for x in range(100,1000):
for y in range(100,1000):
z = (x * y)
k = str(x*y)
if k == k[::-1]:
'''print (k)
print('========================')'''
l.append(k)
if str(z) == str(z)[::-1]:
'''print (z)
print ('+++++++++++++++++++++++++++')'''
j.append(z)
print('*********************************************')
print max(l)
print max(j)
print (l)
l contains strings, j contains integers.
So in the case of l, max performs lexicographic comparison (ignoring numerical value, regular string comparison in this case) whereas in j, max works as intended by taking the maximum integer.
>>> "99999" > "906609"
True
aside: you can compute the maximum value in one line using nested comprehensions passed to max:
max(i for i in (x*y for x in range(100,1000) for y in range(x,1000)) if str(i)==str(i)[::-1])
(note that there's no need to start from 100 for the inner loop, starting from x is enough since multiplication is commutative)

How to see if the list contains consecutive numbers

I want to test if a list contains consecutive integers and no repetition of numbers.
For example, if I have
l = [1, 3, 5, 2, 4, 6]
It should return True.
How should I check if the list contains up to n consecutive numbers without modifying the original list?
I thought about copying the list and removing each number that appears in the original list and if the list is empty then it will return True.
Is there a better way to do this?
For the whole list, it should just be as simple as
sorted(l) == list(range(min(l), max(l)+1))
This preserves the original list, but making a copy (and then sorting) may be expensive if your list is particularly long.
Note that in Python 2 you could simply use the below because range returned a list object. In 3.x and higher the function has been changed to return a range object, so an explicit conversion to list is needed before comparing to sorted(l)
sorted(l) == range(min(l), max(l)+1))
To check if n entries are consecutive and non-repeating, it gets a little more complicated:
def check(n, l):
subs = [l[i:i+n] for i in range(len(l)) if len(l[i:i+n]) == n]
return any([(sorted(sub) in range(min(l), max(l)+1)) for sub in subs])
The first code removes duplicates but keeps order:
from itertools import groupby, count
l = [1,2,4,5,2,1,5,6,5,3,5,5]
def remove_duplicates(values):
output = []
seen = set()
for value in values:
if value not in seen:
output.append(value)
seen.add(value)
return output
l = remove_duplicates(l) # output = [1, 2, 4, 5, 6, 3]
The next set is to identify which ones are in order, taken from here:
def as_range(iterable):
l = list(iterable)
if len(l) > 1:
return '{0}-{1}'.format(l[0], l[-1])
else:
return '{0}'.format(l[0])
l = ','.join(as_range(g) for _, g in groupby(l, key=lambda n, c=count(): n-next(c)))
l outputs as: 1-2,4-6,3
You can customize the functions depending on your output.
We can use known mathematics formula for checking consecutiveness,
Assuming min number always start from 1
sum of consecutive n numbers 1...n = n * (n+1) /2
def check_is_consecutive(l):
maximum = max(l)
if sum(l) == maximum * (maximum+1) /2 :
return True
return False
Once you verify that the list has no duplicates, just compute the sum of the integers between min(l) and max(l):
def check(l):
total = 0
minimum = float('+inf')
maximum = float('-inf')
seen = set()
for n in l:
if n in seen:
return False
seen.add(n)
if n < minimum:
minimum = n
if n > maximum:
maximum = n
total += n
if 2 * total != maximum * (maximum + 1) - minimum * (minimum - 1):
return False
return True
import numpy as np
import pandas as pd
(sum(np.diff(sorted(l)) == 1) >= n) & (all(pd.Series(l).value_counts() == 1))
We test both conditions, first by finding the iterative difference of the sorted list np.diff(sorted(l)) we can test if there are n consecutive integers. Lastly, we test if the value_counts() are all 1, indicating no repeats.
I split your query into two parts part A "list contains up to n consecutive numbers" this is the first line if len(l) != len(set(l)):
And part b, splits the list into possible shorter lists and checks if they are consecutive.
def example (l, n):
if len(l) != len(set(l)): # part a
return False
for i in range(0, len(l)-n+1): # part b
if l[i:i+3] == sorted(l[i:i+3]):
return True
return False
l = [1, 3, 5, 2, 4, 6]
print example(l, 3)
def solution(A):
counter = [0]*len(A)
limit = len(A)
for element in A:
if not 1 <= element <= limit:
return False
else:
if counter[element-1] != 0:
return False
else:
counter[element-1] = 1
return True
The input to this function is your list.This function returns False if the numbers are repeated.
The below code works even if the list does not start with 1.
def check_is_consecutive(l):
"""
sorts the list and
checks if the elements in the list are consecutive
This function does not handle any exceptions.
returns true if the list contains consecutive numbers, else False
"""
l = list(filter(None,l))
l = sorted(l)
if len(l) > 1:
maximum = l[-1]
minimum = l[0] - 1
if minimum == 0:
if sum(l) == (maximum * (maximum+1) /2):
return True
else:
return False
else:
if sum(l) == (maximum * (maximum+1) /2) - (minimum * (minimum+1) /2) :
return True
else:
return False
else:
return True
1.
l.sort()
2.
for i in range(0,len(l)-1)))
print(all((l[i+1]-l[i]==1)
list must be sorted!
lst = [9,10,11,12,13,14,15,16]
final = True if len( [ True for x in lst[:-1] for y in lst[1:] if x + 1 == y ] ) == len(lst[1:]) else False
i don't know how efficient this is but it should do the trick.
With sorting
In Python 3, I use this simple solution:
def check(lst):
lst = sorted(lst)
if lst:
return lst == list(range(lst[0], lst[-1] + 1))
else:
return True
Note that, after sorting the list, its minimum and maximum come for free as the first (lst[0]) and the last (lst[-1]) elements.
I'm returning True in case the argument is empty, but this decision is arbitrary. Choose whatever fits best your use case.
In this solution, we first sort the argument and then compare it with another list that we know that is consecutive and has no repetitions.
Without sorting
In one of the answers, the OP commented asking if it would be possible to do the same without sorting the list. This is interesting, and this is my solution:
def check(lst):
if lst:
r = range(min(lst), max(lst) + 1) # *r* is our reference
return (
len(lst) == len(r)
and all(map(lst.__contains__, r))
# alternative: all(x in lst for x in r)
# test if every element of the reference *r* is in *lst*
)
else:
return True
In this solution, we build a reference range r that is a consecutive (and thus non-repeating) sequence of ints. With this, our test is simple: first we check that lst has the correct number of elements (not more, which would indicate repetitions, nor less, which indicates gaps) by comparing it with the reference. Then we check that every element in our reference is also in lst (this is what all(map(lst.__contains__, r)) is doing: it iterates over r and tests if all of its elements are in lts).
l = [1, 3, 5, 2, 4, 6]
from itertools import chain
def check_if_consecutive_and_no_duplicates(my_list=None):
return all(
list(
chain.from_iterable(
[
[a + 1 in sorted(my_list) for a in sorted(my_list)[:-1]],
[sorted(my_list)[-2] + 1 in my_list],
[len(my_list) == len(set(my_list))],
]
)
)
)
Add 1 to any number in the list except for the last number(6) and check if the result is in the list. For the last number (6) which is the greatest one, pick the number before it(5) and add 1 and check if the result(6) is in the list.
Here is a really short easy solution without having to use any imports:
range = range(10)
L = [1,3,5,2,4,6]
L = sorted(L, key = lambda L:L)
range[(L[0]):(len(L)+L[0])] == L
>>True
This works for numerical lists of any length and detects duplicates.
Basically, you are creating a range your list could potentially be in, editing that range to match your list's criteria (length, starting value) and making a snapshot comparison. I came up with this for a card game I am coding where I need to detect straights/runs in a hand and it seems to work pretty well.

most efficient way to find a sum of two numbers

I am looking into a problem: given an arbitrary list, in this case it is [9,15,1,4,2,3,6], find any two numbers that would sum to a given result (in this case 10). What would be the most efficient way to do this? My solution is n2 in terms of big O notation and even though I have filtered and sorted the numbers I am sure there is a way to do this more efficiently. Thanks in advance
myList = [9,15,1,4,2,3,6]
myList.sort()
result = 10
myList = filter(lambda x:x < result,myList)
total = 0
for i in myList:
total = total + 1
for j in myList[total:]:
if i + j == result:
print i,j
break
O(n log n) solution
Sort your list. For each number x, binary search for S - x in the list.
O(n) solution
For each number x, see if you have S - x in a hash table. Add x to the hash table.
Note that, if your numbers are really small, the hash table can be a simple array where h[i] = true if i exists in the hash table and false otherwise.
Use a dictionary for this and for each item in list look for total_required - item in the dictionary. I have used collections.Counter here because a set can fail if total_required - item is equal to the current item from the list. Overall complexity is O(N):
>>> from collections import Counter
>>> def find_nums(total, seq):
c = Counter(seq)
for x in seq:
rem = total - x
if rem in c:
if rem == x and c[rem] > 1:
return x, rem
elif rem != x:
return x, rem
...
>>> find_nums(2, [1, 1])
(1, 1)
>>> find_nums(2, [1])
>>> find_nums(24, [9,15,1,4,2,3,6])
(9, 15)
>>> find_nums(9, [9,15,1,4,2,3,6])
(3, 6)
I think, this solution would work....
list = [9,15,1,4,2,3,6]
result = 10
list.sort()
list = filter(lambda x:x < result,list)
myMap = {}
for i in list:
if i in myMap:
print myMap[i], i
break
myMap[result - i] = i

Categories