How to make a random but partial shuffle in Python?

Instead of full shuffle , I am looking for a partial shuffle function in python.

Example: "string" should result in "stnrig", but not "nrsgit"

It would be better if I could determine the specific "percentage" of characters that need to be rebuilt.

The goal is to check string comparison algorithms. I want to define a โ€œshuffle percentageโ€, behind which (my) algorithm will mark two (shuffled) lines as completely different.

Update:

Here is my code. Improvements are welcome!

 import random percent_to_shuffle = int(raw_input("Give the percent value to shuffle : ")) to_shuffle = list(raw_input("Give the string to be shuffled : ")) num_of_chars_to_shuffle = int((len(to_shuffle)*percent_to_shuffle)/100) for i in range(0,num_of_chars_to_shuffle): x=random.randint(0,(len(to_shuffle)-1)) y=random.randint(0,(len(to_shuffle)-1)) z=to_shuffle[x] to_shuffle[x]=to_shuffle[y] to_shuffle[y]=z print ''.join(to_shuffle) 
+6
source share
5 answers

Your problem is complex because there are some edge cases to think about:

  • Strings with duplicate characters (i.e. how would you shuffle "aaaab"?)
  • How do you measure character string bindings or rebuild blocks?

In any case, the metric defined to shuffle the rows to a certain percentage is likely to be the same that you use in your algorithm to see how close they are.

My code for shuffling n characters:

 import random def shuffle_n(s, n): idx = range(len(s)) random.shuffle(idx) idx = idx[:n] mapping = dict((idx[i], idx[i-1]) for i in range(n)) return ''.join(s[mapping.get(x,x)] for x in range(len(s))) 

Basically, he selects the positions n for swapping in a random order, and then exchanges each of them with the next one in the list ... Thus, he ensures that reverse swaps will not be created and only the characters n will be replaced (if the characters are repeated there, failure) .

Explained run with 'string', 3 as input:

 idx is [0, 1, 2, 3, 4, 5] we shuffle it, now it is [5, 3, 1, 4, 0, 2] we take just the first 3 elements, now it is [5, 3, 1] those are the characters that we are going to swap string ^ ^ ^ t (1) will be i (3) i (3) will be g (5) g (5) will be t (1) the rest will remain unchanged so we get 'sirgnt' 

The bad thing about this method is that it does not generate all possible options, for example, it cannot make "gnrits" from "string". This could be fixed by shuffling the index partitions as follows:

 import random def randparts(l): n = len(l) s = random.randint(0, n-1) + 1 if s >= 2 and n - s >= 2: # the split makes two valid parts yield l[:s] for p in randparts(l[s:]): yield p else: # the split would make a single cycle yield l def shuffle_n(s, n): idx = range(len(s)) random.shuffle(idx) mapping = dict((x[i], x[i-1]) for i in range(len(x)) for x in randparts(idx[:n])) return ''.join(s[mapping.get(x,x)] for x in range(len(s))) 
+2
source

This problem is simpler than it seems. And the language has the right tools to not remain between you and the idea, as usual:

 import random def pashuffle(string, perc=10): data = list(string) for index, letter in enumerate(data): if random.randrange(0, 100) < perc/2: new_index = random.randrange(0, len(data)) data[index], data[new_index] = data[new_index], data[index] return "".join(data) 
+3
source

maybe so:

 >>> s = 'string' >>> shufflethis = list(s[2:]) >>> random.shuffle(shufflethis) >>> s[:2]+''.join(shufflethis) 'stingr' 

Taking fortran from the idea, I add this to the collection. This is pretty fast:

 def partial_shuffle(st, p=20): p = int(round(p/100.0*len(st))) idx = range(len(s)) sample = random.sample(idx, p) res=str() samptrav = 1 for i in range(len(st)): if i in sample: res += st[sample[-samptrav]] samptrav += 1 continue res += st[i] return res 
+1
source
 import random def partial_shuffle(a, part=0.5): # which characters are to be shuffled: idx_todo = random.sample(xrange(len(a)), int(len(a) * part)) # what are the new positions of these to-be-shuffled characters: idx_target = idx_todo[:] random.shuffle(idx_target) # map all "normal" character positions {0:0, 1:1, 2:2, ...} mapper = dict((i, i) for i in xrange(len(a))) # update with all shuffles in the string: {old_pos:new_pos, old_pos:new_pos, ...} mapper.update(zip(idx_todo, idx_target)) # use mapper to modify the string: return ''.join(a[mapper[i]] for i in xrange(len(a))) for i in xrange(5): print partial_shuffle('abcdefghijklmnopqrstuvwxyz', 0.2) 

prints

 abcdefghljkvmnopqrstuxwiyz ajcdefghitklmnopqrsbuvwxyz abcdefhwijklmnopqrsguvtxyz aecdubghijklmnopqrstwvfxyz abjdefgcitklmnopqrshuvwxyz 
+1
source

The evil and use of the deprecated API:

 import random # adjust constant to taste # 0 -> no effect, 0.5 -> completely shuffled, 1.0 -> reversed # Of course this assumes your input is already sorted ;) ''.join(sorted( 'abcdefghijklmnopqrstuvwxyz', cmp = lambda a, b: cmp(a, b) * (-1 if random.random() < 0.2 else 1) )) 
0
source

Source: https://habr.com/ru/post/902834/


All Articles