59
\$\begingroup\$

According to some controversial story, the odrer of ltteres in a wrod deos not mttaer much for raednig, as lnog as the frist and lsat lteter macth with the orignial wrod.

So, for fun, what would be the shortest function to randomize letter order in a word while keeping the first and the last letter in place?

Here's my stab at it with JavaScript. All whitespace removed it's at 124 130 characters.

function r(w) {
  var l=w.length-1;
  return l<3?w:w[0]+w.slice(1,l).split("").sort(function(){return Math.random()-.5}).join("")+w[l];
}

Shorter JavaScript always welcome.


  • Edit: length check added. Function should not fail for short words.
\$\endgroup\$
18
  • 3
    \$\begingroup\$ Haskell, 4 characters: r=id. \$\endgroup\$ Aug 3, 2011 at 20:01
  • 3
    \$\begingroup\$ Yes. It returns the exact same thing as the input. On another note, what do we do about punctuation? Do we only operate on words consisting only of letters? \$\endgroup\$ Aug 3, 2011 at 20:28
  • 1
    \$\begingroup\$ @trinithis not sure what you talking about, but id is the identity function. I would still like to see Haskell solution to this problem in less than 100 characters. \$\endgroup\$
    – Arlen
    Aug 4, 2011 at 7:37
  • 4
    \$\begingroup\$ Should the specification be updated to require a uniform distribution of outcomes? This would disallow the 4 character Haskell solution. It would also disallow your example Javascript solution (shuffling by doing a sort like that is not uniform). \$\endgroup\$ Aug 4, 2011 at 17:25
  • 2
    \$\begingroup\$ +1 for the first sentence: it actually took me a few seconds to realize it was spelled wrong XP \$\endgroup\$ Aug 4, 2011 at 18:33

82 Answers 82

1 2
3
1
\$\begingroup\$

VBA, 128 177 151 chars

I know this is an older puzzle, but I wanted to add my two cents from

Sub o(s)
n=Left(s,1)
p=Len(s)-1
s=Right(s,p)
For i=1 To p-1
p=p-1
r=Int(p*Rnd()+1)
n=n & Mid(s,r,1)
s=Left(s,r-1) & Right(s,p-r+1)
Next
s=n & s
End Sub

Example Usage:

a = "According"
o a ' a is assigned by the passing of the reference to the 'o' sub.
a = "to"
o a ' see above...
a = "some"
o a
a = "controversial"
o a
a = "story"
o a

I was actually happily surprised to see it fare as well as it did against some of the other, more typical CG languages. After improving, this is still not the shortest, but I was happy to do it.

\$\endgroup\$
3
  • 1
    \$\begingroup\$ Does that keep the first and last letters in place and work with strings less than 3 characters long? \$\endgroup\$
    – Tomalak
    Mar 19, 2012 at 21:34
  • \$\begingroup\$ D'oh! First and last don't keep, but length is not a problem. I'll get back to you... \$\endgroup\$
    – Gaffi
    Mar 19, 2012 at 21:35
  • \$\begingroup\$ Fixed, @Tomalak. Thanks for catching that. \$\endgroup\$
    – Gaffi
    Mar 20, 2012 at 13:10
1
\$\begingroup\$

C# w/Linq - 152 non-whitespace chars.

It's terrible compared to other languages on char count, but elegant:

public string Shuf(string i)
{
    return new String(i.Take(1)
        .Concat(i.Skip(1).Take(i.Length-2).OrderBy(x=>Guid.NewGuid()))
        .Concat(new[]{i.Last()})
        .ToArray());
}
\$\endgroup\$
5
  • \$\begingroup\$ Why is the toArray necessary? \$\endgroup\$
    – Tomalak
    Aug 3, 2011 at 20:22
  • \$\begingroup\$ Because the product of these Linq methods is an IEnumerable<char>, for which there isn't a built-in String constructor. It has to be converted to an explicit char[]. \$\endgroup\$
    – KeithS
    Aug 3, 2011 at 20:35
  • \$\begingroup\$ there's no Rand class in C# - it's Random. Also, name the function S instead of Shuf to gain some chars. \$\endgroup\$ May 15, 2012 at 7:06
  • \$\begingroup\$ also, for a reason I haven't discovered yet, x=>new Random().Next() does not actually randomize the string. Try _=>Guid.NewGuid() - it's shorter and works \$\endgroup\$ May 15, 2012 at 7:11
  • 1
    \$\begingroup\$ It would fail because each new Random() instance is being "seeded" with the same system time value as the last one (or the next one) and so each PRNG produces the same number sequence. \$\endgroup\$
    – KeithS
    May 15, 2012 at 17:10
1
\$\begingroup\$

PHP 7.1, 54 bytes

If the word has less than 4 letters, don´t shuffle:

$r=$w[3]?$w[0].str_shuffle(substr($w,1,-1)).$w[-1]:$w;
\$\endgroup\$
1
\$\begingroup\$

><>, 19 bytes

{o&v
?vo>xl2(
&<oo{

Try it online!

Works for words of less than 3 length.

\$\endgroup\$
1
\$\begingroup\$

R, 93 79 bytes

function(x,l=nchar(x))"if"(l<4,x,intToUtf8(utf8ToInt(x)[c(1,sample(l-2)+1,l)]))

Try it online!

Wroks for any ipnut szie dwon to zero, by frsit ckciheng the lgnteh of the sintrg and rneturing it if nchar(x)<4

-14 bytes thanks to Giuseppe!

\$\endgroup\$
4
  • \$\begingroup\$ 89 bytes changing the sampling a bit \$\endgroup\$
    – Giuseppe
    Jul 17, 2018 at 20:40
  • \$\begingroup\$ Oops, 83 bytes \$\endgroup\$
    – Giuseppe
    Jul 17, 2018 at 20:41
  • \$\begingroup\$ Aaaand another one for good measure: 79 bytes -- in all fairness to me, I haven't gotten much sleep the last few days. \$\endgroup\$
    – Giuseppe
    Jul 17, 2018 at 20:48
  • \$\begingroup\$ @Giuseppe This is much better indeed! \$\endgroup\$
    – JayCe
    Jul 18, 2018 at 0:23
1
\$\begingroup\$

Python 3, 75 73 bytes

Thanks to @WW for saving me 2 bytes

lambda x:x[0]+''.join(sample(x[1:-1],len(x)-2))+x[-1]
from random import*

Try it online!

\$\endgroup\$
4
  • 1
    \$\begingroup\$ You can remove the s= to save 2 bytes. \$\endgroup\$
    – Wheat Wizard
    Jul 18, 2018 at 3:09
  • \$\begingroup\$ @WW I see that you could do that if the import wasn't there but how would you call it in in the "Try it online!" \$\endgroup\$
    – JathOsh
    Jul 18, 2018 at 3:21
  • 1
    \$\begingroup\$ Try it online! \$\endgroup\$
    – Wheat Wizard
    Jul 18, 2018 at 3:22
  • \$\begingroup\$ @WW I see now Thank You! \$\endgroup\$
    – JathOsh
    Jul 18, 2018 at 3:22
1
\$\begingroup\$

JavaScript, 102 bytes

(_,a=[..._].slice(1,-1))=>_.replace(/./g,(m,i)=>(l=a.length,!i||!l?m:a.splice(~~(Math.random()*l),1)))

Try it online!

Using spread syntax and .slice() create array from input string beginning at index 1 through -1, .splice() elements from array within .replace() callback function, return string.

\$\endgroup\$
1
\$\begingroup\$

APL(NARS), 52 chars, 104 bytes

∇r←s w;x
r←,w⋄→0×⍳3≥⍴w⋄r←(↑w),x[?⍨⍴x←¯1↓1↓w],¯1↑w
∇

This 'solution' is based to the fact that 3?3 return a random permutation of 1 2 3;
6?6 a random permutation of 1 2 3 4 5 6 ecc n?n a random permutation of 1 2 .. n so if B is a string B[(⍴B)?⍴B] would be a string random permutation of B. The function 's' has to have as input one string and return one string or ''. If someone see some error, please to say, thank you.

test:

  ⎕fmt s¨'' 'w' 'my' 'the' 'this' 'this' 'this'
┌7────────────────────────────────────────────┐
│┌0┐ ┌1─┐ ┌2──┐ ┌3───┐ ┌4────┐ ┌4────┐ ┌4────┐│
││ │ │ w│ │ my│ │ the│ │ tihs│ │ this│ │ this││
│└¯┘ └──┘ └───┘ └────┘ └─────┘ └─────┘ └─────┘2
└∊────────────────────────────────────────────┘
  s¨'random' 'random' 'random' 'random'
rdonam rodanm raodnm rodnam 
  s 'permutation'
ptaemutroin
\$\endgroup\$
1
\$\begingroup\$

JavaScript, 93 bytes

(_,[a,[...b],c,d=b.sort(_=>Math.random()-.5).join``]=_.match(/(^.)|[^\1]+(?=.$)|.$/g))=>a+d+c

Try it online!

Using .match() with regular expression /(^.)|[^\1]+(?=.$)|.$/g to match first character, one or more characters that at not the first character followed by one character and end of string, and character followed by end of string and .sort() approach used at the question.

\$\endgroup\$
1
\$\begingroup\$

Javascript 98 bytes

a=>{c=l=a.length-1;for(b=[a[0]];--c;)d=Math.random()*99|0,b[d]?c++:b[d]=a[c];return b.join``+a[l]}

Try it online!

This assigns each letter other than the first and last to a random array index, and then joins the array into a string. 99 can be changed to 9e9 to work on (much!) longer words at the cost of a byte, but then join takes almost a minute. Besides, there aren't many words that even approach 100 characters.

Because of that, this is 12 bytes shorter (86 bytes) and works almost every time:

a=>{c=l=a.length-1;for(b=[a[0]];--c;)b[Math.random()*9e9|0]=a[c];return b.join``+a[l]}
\$\endgroup\$
1
\$\begingroup\$

05AB1E, 12 11 bytes

g≠ićs¨.rIθJ

Try it online or verify some more inputs.

Fun 12 11 bytes alternative:

g͸1.ø£€.rJ

Try it online or verify some more inputs.

Both version also work for strings with less than 3 characters. The first one could be just ćs¨.rIθJ (8 bytes), but then it doesn't work for single-character strings ("a" becomes "aa").

Explanation:

g              # Take the length of the (implicit) input
               #  i.e. "howaboutthis" → 12
               #  i.e. "a" → 1
 ≠i            # If this length is not 1:
               #   i.e. 12 → 1 (truthy)
               #   i.e. 1 → 0 (falsey)
   ć           #  Extract the head of the (implicit) input
               #   i.e. "howaboutthis" → "owaboutthis" and "h"
    s          #  Swap so the list (minus head) is at the top of the stack again
     ¨         #  Remove the last character
               #   i.e. "owaboutthis" → "owaboutthi"
      .r       #  Randomly shuffle the characters
               #   i.e. "owaboutthi" → "oohbtwtiua"
        Iθ     #  Take the last character of the input
               #   i.e. "howaboutthis" → "s"
          J    #  Join the values on the stack together (and output implicitly)
               #   i.e. "h", "oohbtwtiua", "s" → "hoohbtwtiuas"
               # (Implicit else)
               #  (Output the input as is implicitly)
               #   i.e. "a"

g              # Take the length of the (implicit) input
               #  i.e. "howaboutthis" → 12
               #  i.e. "a" → 1
 Í             # Subtract 2
               #  i.e. 12 → 10
               #  i.e. 1 → -1
  ¸            # Wrap it into a list
               #  i.e. 10 → [10]
               #  i.e. -1 → [-1]
   1.ø         # Surround it with 1s
               #  i.e. [10] → [1,10,1]
               #  i.e. [-1] → [1,-1,1]
      £        # Split the (implicit) input into parts of that size
               #  i.e. "howaboutthis" and [1,10,1] → ["h","owaboutthi","s"]
               #  i.e. "a" and [1,-1,1] → ["a","",""]
       €       # Map each value to:
        .r     #  Randomly shuffle the characters
               #   i.e. ["h","owaboutthi","s"] → ["h","oohbtwtiua","s"]
               #   i.e. ["a","",""] → ["a","",""]
          J    # Join the values in the list together (and output implicitly)
               #  i.e. ["h","oohbtwtiua","s"] → "hoohbtwtiuas"
               #  i.e. ["a","",""] → "a"
\$\endgroup\$
1
\$\begingroup\$

Pyth, 18 bytes

KhQ=Z>1Q++KO.pPtQZ

Try it online!

\$\endgroup\$
1
\$\begingroup\$

K (ngn/k), 22 bytes

{,/0N?'(0 1,1|#1_x)_x}

Try it online!

hat-tip ovf from the array programming discord / matrix for this answer!

Takes a single word to shuffle/randomize.

  • (...)_x cut x (the input) on the indices from (...), returning a three-item list containing the first element, the middle element(s), and the last element
    • (0 1,1|#1_x) generate a three-item list equivalent to [0, 1, max(1, len(x)-1)]. taking the max with 1 ensures single-character strings (e.g. ,"a") are handled as well.
  • 0N?' shuffle each slice; this is a no-op on empty and one-character strings
  • ,/ flatten the shuffled slices into a single string and (implicitly) return it
\$\endgroup\$
1
\$\begingroup\$

APL, 27 bytes

{3≥⍴⍵:⍵⋄⍵[⍋0,(?⍨2-⍨⍴⍵),⍴⍵]}
\$\endgroup\$
1
\$\begingroup\$

JavaScript, 81 bytes

s=>s.length<4?s:s[0]+[...s.slice(1,-1)].sort(_=>Math.random()>.5).join``+s.at(-1)

Try it:

f=s=>s.length<4?s:s[0]+[...s.slice(1,-1)].sort(_=>Math.random()>.5).join``+s.at(-1)

;[
  '',
  '1',
  '12',
  '123',
  '1234',
  '12345',
  '123456',
  '1234567',
  '12345678',
  '123456789',
].forEach(s=>console.log(f(s)))

JavaScript, 4 bytes

s=>s

It is funny but in real the chance to randomly sort the symbols position in string and get the same string is equal to get the another string. And this is not depend on how many times we will randomize the symbols in string. It may seem like something unreal, but that's the way it is :)

To understand it more easy way imagine that we must generate a number by generating random digits. Let's say we have to get a number of length n. We have 10 digits, so in each position the chance of any digit is \$\frac{1}{10}\$ so the total chance to get the any number with length of n is \$(\frac{1}{10})^n\$. So the chance to get the number 8888888888888888...8888 randomly is the same as the chance to get 9872346897621449...0467. It is important to understand that the probability of the next digit is independent of the previous generated digit

\$\endgroup\$
1
\$\begingroup\$

C (gcc), 71 bytes

Look mom, I beat python!

f(s,d)char*s;{for(;s[2];)if(d=rand()%~-strlen(++s))*s^=s[d]^=*s^=s[d];}

Try it online!

\$\endgroup\$
1
  • \$\begingroup\$ Christ, I was expecting the C answer to be a lot longer than that ... \$\endgroup\$
    – Peter
    Mar 10, 2023 at 6:33
1
\$\begingroup\$

Thunno 2 j, 10 bytes

Dḣ?Ṫsḣµr$h

Attempt This Online!

Explanation

Dḣ?Ṫsḣµr$h  # Implicit input
D           # Duplicate the input
 ḣ          # Pop one copy and check if it's length is more than 1
  ?         # If that's true:
   Ṫ        #  Remove the last character and push it separately
    s       #  Swap so the last character is below the rest of the string
     ḣ      #  Discard the first character from this string
      µr    #  Randomly shuffle the string
        $h  #  Push the first character of the input again
            # Implicit output of joined stack
\$\endgroup\$
0
\$\begingroup\$

Python - 103 96 90 88 85 109 characters

import random as r
def s(x):
 if len(x)>3:y=list(x[1:-1]);r.shuffle(y);return x[0]+''.join(y)+x[-1]
 return x

props to @dr jimbob for the "import as" bit and for catching my non-conformance with the spec. props also to @user unknown for getting the spec clarified.

Edit: updated to conform to short-words spec.

\$\endgroup\$
11
  • \$\begingroup\$ Sorry about the other comment; my mistake; though I still think you need a return x[0]+''.join(y)+x[-1] at the end; also could change y=[x[a] for a in range(1,len(x))] to y=[x[1:-1]] \$\endgroup\$
    – dr jimbob
    Aug 3, 2011 at 17:18
  • \$\begingroup\$ The [1:-1] actually doesn't work, and results in ['hateve'] for input of 'whatever'. My bad on the last letter, good catch. \$\endgroup\$
    – arrdem
    Aug 3, 2011 at 19:28
  • \$\begingroup\$ I meant y=list(x[1:-1]). But you are supposed to not shuffle the first and last letter, so you need to change your return to return x[0]+''.join(y)+x[-1] \$\endgroup\$
    – dr jimbob
    Aug 3, 2011 at 19:55
  • \$\begingroup\$ nice list(). I had [b for b in x[1:-1]] for a while there instead. \$\endgroup\$
    – arrdem
    Aug 3, 2011 at 20:05
  • \$\begingroup\$ Doesn't work for input like 'I'; return 'II'. \$\endgroup\$ Aug 4, 2011 at 3:24
0
\$\begingroup\$

J, 18 bytes

({~>:,~0,1+?~)_2+#

Try it online!

Constructs random indexes for the interior, prepends 0, appends n-1, then uses all those to index into the input.

\$\endgroup\$
0
\$\begingroup\$

GolfScript, 26 bytes

(:f;):l;{;9rand}$f l@\]''+

Try it online!

\$\endgroup\$
0
\$\begingroup\$

Zsh +coreutils, 73 bytes

for n;printf $n[1]$(fold -1<<<${n:1:-1}|shuf|tr -d \\n)${n:$#n-1:$#n-1}\  

Try it online!  78bytes  85bytes  92bytes

Incidentally, the Zsh documentation mixes up the descriptions of flags. F splits, f joins.

\$\endgroup\$
0
\$\begingroup\$

Go, 154 132 bytes

import."math/rand"
func f(S string)(e string){k:=S[1:len(S)-1]
for _,i:=range Perm(len(k)){e+=k[i:i+1]}
return S[:1]+e+S[len(S)-1:]}

Attempt This Online!

  • -22 bytes from rand.Perm, using string slices, and named return.
\$\endgroup\$
1 2
3

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.