How can I scramble a word with a factor? - python

I would like to scramble a word with a factor. The bigger the factor is, the more scrambled the word will become.
For example, the word "paragraphs" with factor of 1.00 would become "paaprahrgs", and it will become "paargarphs" with a factor of 0.50.
The distance from the original letter position and the number of scrambled letters should be taken into consideration.
This is my code so far, which only scrambles without a factor:
def Scramble(s):
return ''.join(random.sample(s, len(s)))
Any ideas?
P.S. This isn't an homework job - I'm trying to make something like this: http://d24w6bsrhbeh9d.cloudfront.net/photo/190546_700b.jpg

You could use the factor as a number of shuffling chars in the string around.
As the factor seem's to be between 0 and 1, you can multiply the factor with the string's length.
from random import random
def shuffle(string, factor):
string = list(string)
length = len(string)
if length < 2:
return string
shuffles = int(length * factor)
for i in xrange(shuffles):
i, j = tuple(int(random() * length) for i in xrange(2))
string[i], string[j] = string[j], string[i]
return "".join(string)
x = "computer"
print shuffle(x, .2)
print shuffle(x, .5)
print shuffle(x, .9)
coupmter
eocpumtr
rpmeutoc
If you want the first and the last characters to stay in place, simply split them and add them later on.
def CoolWordScramble(string, factor = .5):
if len(string) < 2:
return string
first, string, last = string[0], string[1:-1], string[-1]
return first + shuffle(string, factor) + last

You haven't defined what your "factor" should mean, so allow me to redefine it for you: A scrambling factor N (an integer) would be the result of swapping two random letters in a word, N times.
With this definition, 0 means the resulting word is the same as the input, 1 means only one pair of letters is swapped, and 10 means the swap is done 10 times.

You can make the "factor" roughly correspond to the number of times two adjacent letters of the word switch their positions (a transposition).
In each transposition, choose a random position (from 0 through the length-minus-two), then switch the positions of the letter at that position and the letter that follows it.

It could be implemented many ways, but here is my solution:
Wrote a function that just changes a letter's place:
def scramble(s):
s = list(s) #i think more easier, but it is absolutely performance loss
p = s.pop(random.randint(0, len(s)-1))
s.insert(random.randint(0, len(s)-1), p)
return "".join(s)
And wrote a function that apply to a string many times:
def scramble_factor(s, n):
for i in range(n):
s = scramble(s)
return s
Now we can use it:
>>> s = "paragraph"
>>> scramble_factor(s, 0)
'paragraph'
>>> scramble_factor(s, 1)
'pgararaph'
>>> scramble_factor(s, 2)
'prahagrap'
>>> scramble_factor(s, 5)
'pgpaarrah'
>>> scramble_factor(s, 10)
'arpahprag'
Of course functions can be combined or nested, but it is clear I think.
Edit:
It doesn't consider distance, but the scramble function easily replaced just for swapping adjacent letters. Here is one:
def scramble(s):
if len(s)<=1:
return s
index = random.randint(0, len(s)-2)
return s[:index] + s[index + 1] + s[index] + s[index+2:]

You could do a for-loop that counts down to 0.
Convert the String into a Char-Array and use a RNG to choose 2 letters to swap.

Related

I need limit for particular characters in Python itertools

How I can find a way to get all combination with some limits for particular characters. For now I have only limit for all characters. But I want to have character "Q" 4 times in every combinations? Is that possible with my code?
I use itertools combination_with_replacement
from itertools import combinations_with_replacement
import collections
def combine(arr, s):
return [x for x in combinations_with_replacement(symbols, s) if max(collections.Counter(x).values()) <= 3]
symbols = "LNhkPepm3684th"
max_length = 10
set = 10
print(combine(symbols, set))
I notice that your symbols collection contains the letter "h" twice. I'm not sure whether your "must appear 0 or 1 or 2 times, but no more" restriction applies individually to each h, or whether it applies to all "h"es collectively. In other words, is "LLLLLNNNNhh3684hh" a legal result? The "first h" appears twice, and the "second h" appears twice, and so there are four instances of "h" total.
Here's an approach that works if all symbols are individually restricted and "LLLLLNNNNhh3684hh" is a legal result. it works on the principle that any combination of a sequence can be uniquely represented as a list of numbers indicating how many times the element at that index appears in the combination.
def restricted_sum(n, s, restrictions):
"""
Restricted sum problem. Find each list that sums up to a certain number, and obeys restrictions regarding its size and contents.
input:
n -- an integer. Indicates the length of the result.
s -- an integer. Indicates the sum of the result.
restrictions -- a list of tuples. Indicates the minimum and maximum of each corresponding element in the result.
yields:
result -- A list of positive integers, satisfying the requirements:
len(result) == n
sum(result) == s
for i in range(len(result)):
restrictions[i][0] <= result[i] <= restrictions[i][1]
"""
if n == 0:
if s == 0:
yield ()
return
else:
return
else:
if sum(t[0] for t in restrictions) > s: return
if sum(t[1] for t in restrictions) < s: return
l,r = restrictions[0]
for amt in range(l, r+1):
for rest in restricted_sum(n-1, s-amt, restrictions[1:]):
yield (amt,) + rest
def combine(characters, size, character_restrictions):
assert len(characters) == len(set(characters)) #only works for character sets with no duplicates
n = len(characters)
s = size
restrictions = tuple(character_restrictions[c] for c in characters)
for seq in restricted_sum(n, s, restrictions):
yield "".join(c*i for i,c in zip(seq, characters))
symbols = "LNhkPepm3684th"
character_restrictions = {}
#most symbols can appear 0-2 times
for c in symbols:
character_restrictions[c] = (0,2)
#these characters must appear an exact number of times
limits = {"L":5, "N": 4}
for c, amt in limits.items():
character_restrictions[c] = (amt, amt)
for result in combine(symbols, 17, character_restrictions):
print(result)
Result:
LLLLLNNNN8844tthh
LLLLLNNNN6844tthh
LLLLLNNNN6884tthh
LLLLLNNNN68844thh
LLLLLNNNN68844tth
... 23,462 more values go here...
LLLLLNNNNhh3684hh
... 4,847 more values go here...
LLLLLNNNNhhkkPPe6
LLLLLNNNNhhkkPPe3
LLLLLNNNNhhkkPPem
LLLLLNNNNhhkkPPep
LLLLLNNNNhhkkPPee
Add a dictionary that specifies the limit for each character, and uses that instead of 3 in your condition. You can use .get() with a default value so you don't have to specify all the limits.
limits = {'Q': 4, 'A': 2}
def combine(arr, s):
return [x for x in combinations_with_replacement(symbols, s) if max(collections.Counter(x).values()) <= limits.get(x, 3)]

How to generate random increasing or decreasing strings in python?

I need to make sequence of random strings, which increase(decrease) for alphabetic oder. For example: "ajikfk45kJDk", "bFJIPH7CDd", "c".
The simplest thing to do is to create N random strings and then sort them.
So, how do you create a random string? Well, you haven't specified what your rule is, but your three examples are strings of 1 to 12 characters taken from the set of ASCII lowercase, uppercase, and digits, so let's do that.
length = random.randrange(1, 13)
letters = random.choices(string.ascii_letters + string.digits, k=length)
string = ''.join(letters)
So, just do this N times, then sort it.
Putting it together:
chars = string.ascii_letters + string.digits
def make_string():
return ''.join(random.choices(chars, k=random.randrange(1, 13)))
def make_n_strings(n):
return sorted(make_string() for _ in range(n))
This should be simple enough that you can customize it however you want. Want case-insensitive sorting? Just add key=str.upper to the sorted. Want some other distribution of lengths? Just replace the randrange. And so on.
You can use the chr() Python 3 function in a loop while generating random number in the ASCII category range you want.
You can find all the ASCII categories here or on Wikipedia.
For exemple :
chr(99)
-> equals c
More information about the chr() function on Python 3 official documentation.
The simplest way I can think of is
from random import randint
a = ''.join(sorted([chr(randint(33,127)) for i in range(randint(1,20))], reverse = False))
print(a)
reverse = True makes it descending
There's a lot of ways to do that, and this an easy and simple example to do that in Python 3 using Ascii char codes:-
from random import randint
def generateString(minLength, maxLength):
result = "";
resultLength = randint(minLength, maxLength)
for i in range(resultLength):
charType = randint(1,3)
if(charType == 1):
#number
result += chr(randint(48, 57))
elif(charType == 2):
#upper letter
result += chr(randint(65, 90))
elif(charType == 3):
#lower letter
result += chr(randint(97, 122))
return result;
#Example
print(generateString(1,20))

Python Hamming distance rewrite countless for cycles into recursion

I have created a code generating strings which have hamming distance n from given binary string. Though I'm not able to rewrite this in a simple recursive function. There are several sequences (edit: actually only one, the length change) in the for loops logic but I don't know how to write it into the recursive way (the input for the function is string and distance (int), but in my code the distance is represented by the count of nested for cycles. Could you please help me?
(e.g. for string '00100' and distance 4, code returns ['11010', '11001', '11111', '10011', '01011'],
for string '00100' and distance 3, code returns ['11000', '11110', '11101', '10010', '10001', '10111', '01010', '01001', '01111', '00011'])
def change(string, i):
if string[i] == '1':
return string[:i] + '0' + string[i+1:]
else: return string[:i] + '1' + string[i+1:] #'0' on input
def hamming_distance(number):
array = []
for i in range(len(number)-3): #change first bit
a = number
a = change(a, i) #change bit on index i
for j in range(i+1, len(number)-2): #change second bit
b = a
b = change(b, j)
for k in range(j+1, len(number)-1): #change third bit
c = b
c = change(c, k)
for l in range(k+1, len(number)): #change fourth bit
d = c
d = change(d, l)
array.append(d)
return array
print(hamming_distance('00100'))
Thank you!
Very briefly, you have three base cases:
len(string) == 0: # return; you've made all the needed changes
dist == 0 # return; no more changes to make
len(string) == dist # change all bits and return (no choice remaining)
... and two recursion cases; with and without the change:
ham1 = [str(1-int(string[0])) + alter
for alter in change(string[1:], dist-1) ]
ham2 = [str[0] + alter for alter in change(string[1:], dist) ]
From each call, you return a list of strings that are dist from the input string. On each return, you have to append the initial character to each item in that list.
Is that clear?
CLARIFICATION
The above approach also generates only those that change the string. "Without" the change refers to only the first character. For instance, given input string="000", dist=2, the algorithm will carry out two operations:
'1' + change("00", 2-1) # for each returned string, "10" and "01"
'0' + change("00", 2) # for the only returned string, "11"
Those two ham lines go in the recursion part of your routine. Are you familiar with the structure of such a function? It consists of base cases and recursion cases.

how to make an imputed string to a list, change it to a palindrome(if it isn't already) and reverse it as a string back

A string is palindrome if it reads the same forward and backward. Given a string that contains only lower case English alphabets, you are required to create a new palindrome string from the given string following the rules gives below:
1. You can reduce (but not increase) any character in a string by one; for example you can reduce the character h to g but not from g to h
2. In order to achieve your goal, if you have to then you can reduce a character of a string repeatedly until it becomes the letter a; but once it becomes a, you cannot reduce it any further.
Each reduction operation is counted as one. So you need to count as well how many reductions you make. Write a Python program that reads a string from a user input (using raw_input statement), creates a palindrome string from the given string with the minimum possible number of operations and then prints the palindrome string created and the number of operations needed to create the new palindrome string.
I tried to convert the string to a list first, then modify the list so that should any string be given, if its not a palindrome, it automatically edits it to a palindrome and then prints the result.after modifying the list, convert it back to a string.
c=raw_input("enter a string ")
x=list(c)
y = ""
i = 0
j = len(x)-1
a = 0
while i < j:
if x[i] < x[j]:
a += ord(x[j]) - ord(x[i])
x[j] = x[i]
print x
else:
a += ord(x[i]) - ord(x[j])
x [i] = x[j]
print x
i = i + 1
j = (len(x)-1)-1
print "The number of operations is ",a print "The palindrome created is",( ''.join(x) )
Am i approaching it the right way or is there something I'm not adding up?
Since only reduction is allowed, it is clear that the number of reductions for each pair will be the difference between them. For example, consider the string 'abcd'.
Here the pairs to check are (a,d) and (b,c).
Now difference between 'a' and 'd' is 3, which is obtained by (ord('d')-ord('a')).
I am using absolute value to avoid checking which alphabet has higher ASCII value.
I hope this approach will help.
s=input()
l=len(s)
count=0
m=0
n=l-1
while m<n:
count+=abs(ord(s[m])-ord(s[n]))
m+=1
n-=1
print(count)
This is a common "homework" or competition question. The basic concept here is that you have to find a way to get to minimum values with as few reduction operations as possible. The trick here is to utilize string manipulation to keep that number low. For this particular problem, there are two very simple things to remember: 1) you have to split the string, and 2) you have to apply a bit of symmetry.
First, split the string in half. The following function should do it.
def split_string_to_halves(string):
half, rem = divmod(len(string), 2)
a, b, c = '', '', ''
a, b = string[:half], string[half:]
if rem > 0:
b, c = string[half + 1:], string[rem + 1]
return (a, b, c)
The above should recreate the string if you do a + c + b. Next is you have to convert a and b to lists and map the ord function on each half. Leave the remainder alone, if any.
def convert_to_ord_list(string):
return map(ord, list(string))
Since you just have to do a one-way operation (only reduction, no need for addition), you can assume that for each pair of elements in the two converted lists, the higher value less the lower value is the number of operations needed. Easier shown than said:
def convert_to_palindrome(string):
halfone, halftwo, rem = split_string_to_halves(string)
if halfone == halftwo[::-1]:
return halfone + halftwo + rem, 0
halftwo = halftwo[::-1]
zipped = zip(convert_to_ord_list(halfone), convert_to_ord_list(halftwo))
counter = sum([max(x) - min(x) for x in zipped])
floors = [min(x) for x in zipped]
res = "".join(map(chr, floors))
res += rem + res[::-1]
return res, counter
Finally, some tests:
target = 'ideal'
print convert_to_palindrome(target) # ('iaeai', 6)
target = 'euler'
print convert_to_palindrome(target) # ('eelee', 29)
target = 'ohmygodthisisinsane'
print convert_to_palindrome(target) # ('ehasgidihmhidigsahe', 84)
I'm not sure if this is optimized nor if I covered all bases. But I think this pretty much covers the general concept of the approach needed. Compared to your code, this is clearer and actually works (yours does not). Good luck and let us know how this works for you.

Rough string alignment in python

If I have two strings of equal length like the following:
'aaaaabbbbbccccc'
'bbbebcccccddddd'
Is there an efficient way to align the two such that the most letters as possible line up as shown below?
'aaaaabbbbbccccc-----'
'-----bbbebcccccddddd'
The only way I can think of doing this is brute force by editing the strings and then iterating through and comparing.
Return the index which gives the maximum score, where the maximum score is the strings which have the most matching characters.
def best_overlap(a, b):
return max([(score(a[offset:], b), offset) for offset in xrange(len(a))], key=lambda x: x[0])[1]
def score(a, b):
return sum([a[i] == b[i] for i in xrange(len(a))])
>>> best_overlap(a, b)
5
>>> a + '-' * best_overlap(a, b); '-' * best_overlap(a, b) + b
'aaaaabbbbbccccc-----'
'-----bbbebcccccddddd'
Or, equivalently:
def best_match(a, b):
max = 0
max_score = 0
for offset in xrange(len(a)):
val = score(a[offset:], b)
if val > max_score:
max_score = val
max = offset
return max
There is room for optimizations such as:
Early exit for no matching characters
Early exit when maximum possible match found
I'm not sure what you mean by efficient, but you can use the find method on str:
first = 'aaaaabbbbbccccc'
second = 'bbbebcccccddddd'
second_prime = '-'* first.find(second[0]) + second
first_prime = first + '-' * (len(second_prime) - len(first))
print first_prime + '\n' + second_prime
# Output:
# aaaaabbbbbccccc-----
# -----bbbebcccccddddd
I can't see any other way than brute forcing it. The complexity will be quadratic in the string length, which might be acceptable, depending on what string lengths you are working with.
Something like this maybe:
def align(a, b):
best, best_x = 0, 0
for x in range(len(a)):
s = sum(i==j for (i,j) in zip(a[x:],b[:-x]))
if s > best:
best, best_x = s, x
return best_x
align('aaaaabbbbbccccc', 'bbbebcccccddddd')
5
I would do something like the binary & function on each of your strings. Compares each of the strings when they are lined up, counting up the number of times letters match. Then, shift by one and do the same thing, and go on and on with shifting until they are no longer lined up. The shift with the most matching letters in this fashion is the correct output shift, and you can add the dashes when you print it out. You don't actually have to modify the strings for this, just count the number of shifts and offset your comparing of the characters by that shift amount. This is not terribly efficient (O(n^2) = n+(n-2)+(n-4)...), but is the best I could come up with.

Categories