[Question]
  [
This is much more advanced than [How to randomize letters in a word](https://codegolf.stackexchange.com/questions/3293/how-to-randomize-letters-in-a-word) and [Cambridge Transposition](https://codegolf.stackexchange.com/questions/9261/cambridge-transposition) because of the rule about which letters may be swapped with which. A simple regex will not suffice here.
---
It is well known that a text can still be read while the innards of its words have been scrambled, as long as their first and last letters plus their overall outlines remain constant. Given a printable Ascii+Newline text, scramble each word according to these rules:
1. Scrambling must be (pseudo) random.
2. A word is a sequence of the Latin characters, A through Z.
3. Only initial letters will ever be uppercase.
4. The first and last letters must stay untouched.
5. When scrambling, only letters within one of the following groups may exchange places:
	1. `acemnorsuvwxz`
	2. `bdfhkl`
	3. `gpqy`
	4. `it`
	5. `j` (stays in place)
### Example
> 
> # Srcmable wrods while psrrnveieg their oeiltnus
> 
> 
> It is well known that a txet can still be read while the inrands of its wrods have been srcambled, as long as their fisrt and last letters plus their ovaerll ontliues raemin canstnot. Given a patnirlbe Acsii+Nwnliee txet, samrclbe ecah word anoccdirg to these relus:
> 
> 
> 1. Smncrbliag must be (pusedo) rondam.
> 2. A wrod is a seqencue of the Latin chreratacs, A thurogh Z.
> 3. Only iniital lrttees will eevr be uppcsaere.
> 4. The fisrt and lsat lettres must stay uctoenhud.
> 5. When sarnclbimg, only letters wihtin one of the fwllnoiog guorps may ecxhange plaecs:
> 
> 
> 	1. `aneusvrowxmcz`
> 	2. `bhkfdl`
> 	3. `gqpy`
> 	4. `it`
> 	5. `j` (stays in plcae)
> 
> 
> ### Emxaple
> 
> 
> 
  
[Answer]
# [Jelly](https://github.com/DennisMitchell/jelly), ~~80~~ 74 [bytes](https://github.com/DennisMitchell/jelly/wiki/Code-page)
-2 bytes by moving from `czar + vex + mow + sun` to `czar + vexes + unmown` (the repeated `e`s and `n`s are not a problem)  
-1 byte using `Tị` rather than `ȦÐf`  
-1 byte using `Œle€Øa` rather than `i@€ØB>⁵`  
-2 bytes by reconfiguring the layout a little
```
Tị
TẊị⁹ż@œp
e€ç⁸F
W;“HọƊṘ€.`]HɲøƁḤ0ẉlfrøj⁷»Ḳ¤ç/
Ḣ,ṪjÇḟ0
Œle€Øað¬œpÇ€ÑżœpÑ¥
```
A full program taking a character list (or Python formatted string), which prints the result of the scrambling.
**[Try it online!](https://tio.run/nexus/jelly#dVE/bxMxFN/vU7wRpHCUfwssMABFQmIgUiUkJF58Lzm3Pvtq@5KUibBUtGIoLB2QkFBRJdQBdeHSdMoplfgYl@/AHJ6TkEx4sZ/93u@fZ816dBg164sD3qeD4eTy4eRzHtH0/Vl1Oh2UT6KtB9N3Xzbr0cerg3p4zPfxm9ebv8@r8mpQlycb9cUH1bZVuT0d/BqP6vJ8fFKd3ozq8lujHv7Yrvbr8utGNPmk5pDHWP0cnzFDtR/Ko8llOB@Nv89ms2cepIMeKQU72vQ0@BQ9IHjqexCowXnJby0CS5hAL5WKuIdAao02cWDaID0jmFCk2CXuJR4TFrOWoqQB6EAZ3Qk7D0oLbWkdc@gEFPJBkfdkHeSq@NdhumSRaU3hldTkmDxDqUEY7TxqH8NT2WWWhc7Gig0IRTrXAigEb5J5vQmojh0Uitz9KAJet2J4uRgKLVnBOtjjtdxRkZjrYFmdyeJF7@0YHi1AOSoER7sFaUHBekjiOfogLUWLIhhpcLdPrSk6KbxaQtyJ4YVWe5ya9BLVynMvhEvsNrAXeU5WoKPl0N0Ymoz/n7jmmjmNPSi0N4VIKVnO3YthK13/ARtsgAnsa1afsmSjVx7aRinTC1F0WHjO6IxLfTalO8Q/g2KVXFhcZtpYV3R7/bfr61bSTnfUuu7ku3vrSvooetzHLFf0R5sbgr@K/gI)**
A huge amount of difficulty for Jelly here it seems (either that or I have missed a trick, which has been known to happen!) This will surely be beaten by languages with better string manipulation like ~~[Retina](https://github.com/m-ender/retina)~~(no random functionality) or [05ab1e](https://github.com/Adriandmen/05AB1E).
### How?
```
Tị - Link 1, get truthy items: list a
T  - truthy indexes of a
 ị - index into a
TẊị⁹ż@œp - Link 2, selective shuffle: list a, list b
T        - truthy indexes of a (those indexes that may be shuffled in b)
 Ẋ       - random shuffle
   ⁹     - link's right argument, b
  ị      - index into (gets the shuffled values)
      œp - partition b at truthy indexes of a
    ż@   - zip with reversed @rguments (place shuffled values - yields a list of lists)
e€ç⁸F - Link 3, value selective shuffle: list a, list b
e€    - c exists in b? for €ach c in a (1s where b has shuffle-able characters, else 0s)
   ⁸  - link's left argument, a
  ç   - call the last link (2) as a dyad
    F - flatten the result (from the yielded list of lists to one list)
W;“HọƊṘ€.`]HɲøƁḤ0ẉlfrøj⁷»Ḳ¤ç/ - Link 4, perform all shuffles on a word's innards: list x
W                             - wrap x in a list
                          ¤   - nilad followed by link(s) as a nilad:
  “HọƊṘ€.`]HɲøƁḤ0ẉlfrøj⁷»     -   compression of s(bdfhkl)+d( czar)+d(vexes)+d(unmown)+s( gpqy)+d( ti)
                              -     where d() looks up a word in Jelly's dictionary and s() adds a string to the compressed output.
                         Ḳ    -   split on spaces: ["bdfhkl","czarvexesunmown","gpqy","ti"]
                           ç/ - reduce by last link (3) as a dyad (shuffles by each in turn)
Ḣ,ṪjÇḟ0 - Link 5, shuffle a word: list w
Ḣ       - head w (yields the leftmost character and modifies w)
  Ṫ     - tail w (yields the rightmost character and modifies w)
 ,      - pair
        -   Note: head and tail yield 0 when w is empty, so ['a'] -> ["a",0] and [] -> [0,0]
    Ç   - call the last link (4) as a monad (with the modified w)
   j    - join
     ḟ0 - filter discard zeros (thus single or zero letter words pass through unchanged)
Œle€Øað¬œpÇ€ÑżœpÑ¥ - Main link: list s
Œl                 - convert s to lowercase, say t
    Øa             - lowercase alphabet, say a
  e€               - c exists in a? for €ach c in t
      ð            - dyadic chain separation (call that u)
       ¬           - not (vectorises across u), say v
        œp         - partition s at truthy indexes of v (extract words, plus empty lists from within strings of non-alphabetic characters)
          Ç€       - call the last link (5) as a monad for €ach (shuffle their innards)
            Ñ      - call the next link (1) as a monad (only keep the actual words)
                 ¥ - last two links as a dyad:
              œp   -   partition s at truthy indexes of u (get the non-words, plus empty lists from within strings of alphabetic characters)
                Ñ  -   call the next link (1) as a monad (only keep actual non-words)
             ż     - zip together
                   - implicit print
```
[Answer]
# PHP, 278 Bytes
```
<?=preg_replace_callback("#\pL\K(\pL+)(?=\pL)#",function($t){preg_match_all("#([^bdf-lpqty])|([bdfhkl])|([gpqy])|([it])|(j)#",$t[0],$p);foreach($p as$v){$k++?$c=array_keys($n=array_filter($v)):$o=[];!$n?:shuffle($n)&&$o+=array_combine($c,$n);}ksort($o);return join($o);},$argn);
```
[Try it online!](https://tio.run/nexus/php#LVBBbsIwELzzijSskK0Eqddi0jygvfUYaOQYJzYxtnEcEKI8u2e6pFx2Z3ZnVqtZl175GfDQ2SL9EoEfGiOTswu7ITkrjdgHOchw0rZLopI6JG6MRls5pGzWukAY6PUbliyj11n5fl@XBVq6OkhvuJC14MY0XPQknW/85@aDYM0oKQvsdJ7m7WhF1M4SiPQ6OQ88ClWjDS2k@m527dL4Y7xs6Q@pkKneTLDzx/@Zjo@2f1yDWL1uc/CU4W@SC0XAJ3yAE71Cn2UliIKHwC91Ly8DAftkrTZRBoIyugJXVFv2ArZcDWpsWyNRRxcLcNlTLdyhwQQIiBw37NYPLkQCjrIg4xhssnfaTvyWT9FSdpdCuXRjMbTbAyaIp1XK7r/WLQW@Kv8A "PHP – TIO Nexus")
Expanded
```
echo preg_replace_callback("#\pL\K(\pL+)(?=\pL)#" # patter \pL is shorter as [a-z]
,function($t){  # replacement function beginning
  preg_match_all("#([^bdf-lpqty])|([bdfhkl])|([gpqy])|([it])|(j)#",$t[0],$p); # makes groups with the regex. group 0 is the whole substring
  foreach($p as$v){ # loop through groups
    $k++?$c=array_keys($n=array_filter($v)):$o=[]; # group 0 make new empty replacement array in the other case filter the group remove empty values. 
    #You gain an array with the keys as position in the substring and the values
    #store the key array and the values array
    !$n?:shuffle($n)&&$o+=array_combine($c,$n); 
    #if values shuffle the values and make a new array with the keys and the shuffled values and merge the new array to the replacement array
  }
  ksort($o); # sort the replacement array ascending positions 
  return join($o); # return the replacement as string
},$argn);
```
functions
[array\_combine](http://php.net/manual/en/function.array-combine.php)
[array\_filter](http://php.net/manual/en/function.array-filter.php)
[array\_keys](http://php.net/manual/en/function.array-keys.php)
[ksort](http://php.net/manual/en/function.ksort.php)
[preg\_replace\_callback](http://php.net/manual/en/function.preg-replace-callback.php)
[shuffle](http://php.net/manual/en/function.shuffle.php)
[Answer]
# [Pyth](https://github.com/isaacg1/pyth), 79 bytes
```
sm?td++hduuXNhTeTC,f@@GTHUG.S@HGG+-GJ."by❤jã~léÜº"cJ\jPtdedd:jb.z"([A-Za-z]+)"3
```
where `❤` is U+0018.
[Try it online!](http://pyth.herokuapp.com/?code=sm%3Ftd%2B%2BhduuXNhTeTC%2Cf%40%40GTHUG.S%40HGG%2B-GJ.%22by%18j%C3%A3~l%C3%A9%C3%9C%C2%BA%22cJ%5CjPtdedd%3Ajb.z%22%28%5BA-Za-z%5D%2B%29%223&input=It+is+well+known+that+a+text+can+still+be+read+while+the+innards+of+its+words+have+been+scrambled%2C+as+long+as+their+first+and+last+letters+plus+their+overall+outlines+remain+constant.+Given+a+text%2C+scramble+each+word+according+to+these+rules%3A%0A%0A1.+Scrambling+must+be+%28pseudo%29+random.%0A%0A1.+A+word+is+a+sequence+of+the+Latin+characters%2C+A+through+Z.%0A%0A1.+Only+initial+letters+will+ever+be+uppercase.%0A%0A1.+The+first+and+last+letters+must+stay+untouched.%0A%0A1.+When+scrambling%2C+only+letters+within+one+of+the+following+groups+may+exchange+places%3A%0A%0A+1.+%60acemnorsuvwxz%60%0A%0A+1.+%60bdfhkl%60%0A%0A+1.+%60gpqy%60%0A%0A+1.+%60it%60%0A%0A+1.+%60j%60+%28stays+in+place%29&debug=0)
## Sample
It is well knwon that a text can still be raed while the irnands of its wrods have been seraclbmd, as long as their first and last lettres plus their oaervll ontliues rmeain conntsat. Given a text, sacmrble each wrod acrncdiog to thsee relus:
1. Scamrlbing must be (puesdo) rnadom.
2. A word is a suqencee of the Latin chraectars, A thuorgh Z.
3. Only iaitinl lettres will eevr be uppaersce.
4. The first and last lettres msut stay uotcnuhed.
5. When srancblimg, only lettres wiihtn one of the follnwiog guorps may enxhcage plecas:
	1. `amsuvrcnoxewz`
	2. `bhfkdl`
	3. `gpqy`
	4. `it`
	5. `j` (stays in place)
[Answer]
# JavaScript 176 bytes
```
t.replace(/\B(\w+)\B/g,b=>{return[/[acemnorsuvwxz]/g,/[bdfhkl]/g,/[gpqy]/g,/[it]/g].forEach(d=>{g=b.match(d),b=b.replace(d,c=>{return g.splice(Math.random()*g.length,1)})}),b})
```
**Method:**
1. RegExp iterates over the centre of each word (`/\B(\w+)\B/g`) using 1st replace fn.
2. 1st replace fn iterates an array of RegExp's for each letter-group (`/[bdfkhl/g, /[gqpy]/g, etc..` ).
3. Each iteration builds a temp array of word-centre's characters appearing in current letter-group.
4. Each iteration then uses current letter-group's RegExp to iterate over entire word-centre, using a 2nd replace fn.
5. 2nd replace fn randomly splices the temp array, removing a random character and returning it.
**Demo:**
Run it in JSFiddle: <https://jsfiddle.net/CookieJon/bnpznb7r/>
[Answer]
# C, ~~453, 356~~ 369 bytes
```
#define F for
#define M rand()%s+1+q
char a[256],*b=" acemnorsuvwxz\1bdfhkl\1gpqy\1it\1j";g(c,t)char*c,*t;{static int i,j,k,w,v,n,q,s,r;r=-1;if(c&&t){strcpy(c,t);if(!k)F(j=i=k=1;b[i];++i)b[i]-1?(a[b[i]]=j):++j;F(r=i=0;c[i];){F(;isspace(c[i]);++i);F(q=i;!isspace(c[i])&&c[i];++i);F(s=v=i-q-2;--v>0;)if(a[c[j=M]]==a[c[w=M]]&&a[c[j]])n=c[j],c[j]=c[w],c[w]=n;}}return r;}
```
ungolf with comments
```
// Input in the arg "t" result in the arg "c"
// NB the memory pointed from c has to be >= memory pointed from t
//    the char is 8 bit
#define F for
#define M rand()%s+1+q
char a[256], *b=" acemnorsuvwxz\1bdfhkl\1gpqy\1it\1j";
   g(c,t)char*c,*t;
   {static int i,j,k,w,v,n,q,s,r;
    r=-1;
    if(c&&t)
      {strcpy(c,t);                         // copy the string in the result space
       if(!k)
         F(j=i=k=1;b[i];++i)
             b[i]-1?(a[b[i]]=j):++j;        // ini [possible because at start k=0]
       F(r=i=0;c[i];)
         {F(;isspace(c[i]);++i);            //skip spaces
                                            // the start q the end+1 i
          F(q=i;!isspace(c[i])&&c[i];++i);  //skip word
          F(s=v=i-q-2;--v>0;)               //loop for swap letters of the same set
            if(a[c[j=M]]==a[c[w=M]]&&a[c[j]])
                n=c[j],c[j]=c[w],c[w]=n;
         }
      }
   return r;
  }
#include <stdio.h>
#define G(x,y) if(x)goto y
main()
{char a[256],r[256];
l1:
 gets(a);// i would know the string lenght<256
 g(r,a);
 printf("%s\n",r);
 G(*a,l1);
}
```
[Answer]
# Python 3.6, ~~349~~ 340 bytes
```
from itertools import *
from random import *
import re
def S(s):
    C=lambda c:len(list(takewhile(lambda x:c not in x,('j','it','gqpy','bhkfdl'))));L=[];B=[[]for i in range(5)]
    for l in s:c=C(l);L+=[c];B[c]+=[l];shuffle(B[c])
    return''.join(B[n].pop()for n in L)
A=lambda t:re.sub('[A-Za-z]{3,}',lambda x:x[0][0]+S(x[0][1:][:-1])+x[0][-1],t)
```
Indented with tabs.
The function is named `A`.
It doesn't use brute force, the runtime is deterministic, as OP asked.
[Answer]
## Mathematica 232 Bytes
```
StringReplace[#,x:Repeated[WordCharacter,{2,∞}]:>""<>(s=StringTake)[x,{i,i}~Table~{i,StringLength@x}/.Flatten[Thread[#->RandomSample@#]&/@(StringPosition[x~s~{2,-2},#]+1&/@Characters@{"acemnorsuvwxz","bdfhkl","gpqy","it","j"})]]]&
```
The basic idea is to permute the subsets corresponding to the 4 distinct character groups. Probably room for improvement.
[Answer]
# JavaScript (ES6), ~~380~~ ~~327~~ ~~311~~ 294 Bytes
(~~298~~ ~~282~~ 265 Bytes excluding the rules)
Thanks to @Shaggy for the useful tips!
```
((b,d)=>b.replace(/\B[a-z]+\B/gi,f=>(g=>(g.map(j=>(h=d.slice(0,~(rind=d.indexOf(j))?rind:-1),~rind?h.split`,`.length-1:-1)).map((j,k,l,m=[])=>{l.map((n,o)=>n==j?m.push(o):0),sub=m[new Date%(m.length-1)]||k,tmp=g[sub],g[sub]=g[k],g[k]=tmp}),g.join``))([...f])))(s,"aneusvrowxmcz,bhkfdl,gqpy,it");
```
```
var f = ((b,d)=>b.replace(/\B[a-z]+\B/gi,f=>(g=>(g.map(j=>(h=d.slice(0,~(rind=d.indexOf(j))?rind:-1),~rind?h.split`,`.length-1:-1)).map((j,k,l,m=[])=>{l.map((n,o)=>n==j?m.push(o):0),sub=m[new Date%(m.length-1)]||k,tmp=g[sub],g[sub]=g[k],g[k]=tmp}),g.join``))([...f])))
var s="Let there be scrambling";
console.log(s);
console.log(f(s,"aneusvrowxmcz,bhkfdl,gqpy,it"))
s="It is well known that a text can still be read while the innards of its words have been scrambled, as long as their first and last letters plus their overall outlines remain constant. Given a printable Ascii+Newline text, scramble each word according to these rules";
console.log(s);
console.log(f(s,"aneusvrowxmcz,bhkfdl,gqpy,it"))
```
Function *f* takes in a string of any kind (single word, multiple words, multiple words with signs in it - which it interprets as word-breaking) and ~~an array~~ a string of "rules" of any length separated by commas.
That array of rules, in the case of your question, would be ~~`["aneusvrowxmcz", "bhkfdl", "gqpy", "it"]`~~ `"aneusvrowxmcz,bhkfdl,gqpy,it"`
Some letters don't get mixed even though they could, since you stated in your question that letters "may exchange spaces". If I misinterpreted it, I can change the code to always scramble letters that match the rules.
I know this is an enormous amount of bytes and it won't be able to compete with golfing languages, but I wanted to try anyway, hope you like it :)
Human-readable non-uglified code:
```
((txt,rules)=>txt.replace(/\B[a-z]+\B/gi,wo=>((w=>(w.map(c=>(h=rules.slice(0, ~(rind=rules.indexOf(c))?rind:-1),~rind?(h.split`,`.length-1):-1)).map((e,i,arr,a=[])=>{
    arr.map((x,i)=>(x==e)?a.push(i):0),
    sub=a[new Date%(a.length-1)]||i,
    tmp=w[sub],
    w[sub]=w[i],
    w[i]=tmp
}),w.join``))([...wo]))))(str, "aneusvrowxmcz,bhkfdl,gqpy,it")
```
[Answer]
# [C (clang)](http://clang.llvm.org/), ~~282~~ 263 bytes
-19 bytes thanks to the man (or cat) ceilingcat!!
```
c,o,d,e,g;l(*f){for(char*s[5]={"aneusvrowxmcz","bhkfdl","gqpy","it"},**h,*i=f,*t;*i;){if(isalpha(*i)){for(t=i;isalpha(*i);)i++;e=i-t-2;for(h=s;*h&&e;*h++)for(c=999;--c;)if(o=1+rand(d=1+rand()%e)%e,index(*h,t[d])&&index(*h,t[o]))g=t[d],t[d]=t[o],t[o]=g;}else++i;}}
```
[Try it online!](https://tio.run/##dVNPb9MwFD93n@KpElWcpB0b7DBCDpwQEhIHkJDoenCdl9jMtVPbadpV/eqU51Rry4Eoiu3n59@/JGIqNDfN8Shym1c55k2hk7Rm@9q6REjuUj9/WJT7MTfY@Y2z/XYlXsb5eCmf60rTpFm3OxpUGB/yNJV5qso6T0ORqoLtVZ0oz3UreZIqdkINpSquigVTWVZgqaZhel/EBln6IpWTCdIzy9igpHx8fCymU0HddWLLu8xxUyXV64S9QbpzZSrcJiQizKsFm0yu1nbBWFPG@rBZxspQLpvigNpjlqnicDiuuDIJ28M/180oRgFhvoCSFqPxlwDKQ49aw7OxvYEgeQAOAbcBBDfgg6K9JYJDXkEvlUbqQVDGcFd5sDWoQAg2LiTfIPWigXEE98Lx1VJjlQP3oK1p4kinlYNaOU9EpgLNaaIxBHQeWt29dtgNOk7ctgtaGfSkIHo6QQtrfOAmzOCz2hDfSXEOr5SAXMhBFXAhaFBEHmyE9uSl0@g/PJknM4DdzeD76VzsWnWkhwwnrceusgzii7Gr2bn9fgafTtAUHQeP6w6NwBhFTOYrD6Qy5sxF9JRTd5DOdo2EXxeUdzP4ZvSOglRBcX1OoI95I3mPGrq2RSe4x8u59zP4QSz/yW8QT8nsoDPBdkJidTn6MIOfksLyZ7M52Kjhwh0kabfmbKa2Wts@xtKQg5YICBq35M40SG@LiyHIAT5@YBQllVbGOt9t@u3L1RbFtqxq@ayvahRC0653VxWyp8LVmjT/hiQ68hTViZHRfnEz8sMfE9QKk7eMFXB7Cx/B2EAfyrpTDqubkU4Co862C36YHY5/RK1544/T/i8 "C (clang) – Try It Online")
Ungolfed:
```
int func(char*p) 
{
    char *groups[] = {"aneusvrowxmcz","bhkfdl","gqpy","it",0}, **g, *s, *t;
    int n,r,i,l,o;
    for (s = p; *s;)
    {
        if (isalpha(*s))
        {
            t = s;
            while (*s && isalpha(*s))
                s++;
            // start scrambling
            l = s - t - 2;
            for(g=groups; *g && l; *g++)
            {
                for(n=999;--n;)
                {
                    i = 1 + rand() % l;
                    r = 1 + rand() % l;
                    if (strchr(*g, t[i]) && strchr(*g, t[r]))
                    {
                        o=t[i];
                        t[i]=t[r];
                        t[r]=o;
                    }
                }
            }
            // end scrambling
        }
        else 
            s++;
    }
}
```
[Answer]
# JavaScript, 151 bytes
Revisiting and expanding on my suggested improvements to [Bumpy's solution](https://codegolf.stackexchange.com/a/120019/58974) all these years later.
```
s=>s.replace(/\B[a-z]+\B/g,m=>[/[^bdf-jklpqty]/g,/[bdfhkl]/g,/[gpqy]/g,/i|t/g].map(r=>m=m.replace(r,c=>d.sort(_=>new Date%3-1).pop(),d=m.match(r)))&&m)
```
[Try it online!](https://tio.run/##dVLNbhMxEL7vU8yFalfdOCoFCSFtpFZICAnBASQk0kCn3smuW6/t2rPZtuLAs/BovEgYJ0p64uTxePz92be4waSjCTzbvNmum21qFklFChY1lfOryyXOnlanV5fzrh6axXK@/HHTrme3dzbc8@NKuvOlNPo7u6@7cL/vml8871ZqwFDGZjE0wxE01rpZtCr5yOXPZuFognfI9OJ8dlap4ENZ1a2MD8i6L2NVVScnQ7XV3iVvSVnfleuyuP7AYBJMZC3cOT854B4ZEJgeGDQ6SGzk7IYgErYw9caSzBAY5zC2CfwaDAuCz5seNySzJNd0xOHGUlsDJrDedXmViybC2sQkHK4Fi1JYYqaYINjxMOE3FFFo/cjWOEpCPqBxkNUzOlbw3myEBSFE4xiFCC6SNub0E035xk5/fVQBhLrfaQTUWhYjethntiTORkvpbXGm4Mt@Pp8Oo0gT22VINLa@giiC/aCKlwou9lASHEKi@5GcphxEzuUjchbaY0SdbdUyzX30Y9fDd1WcK/js7KPEZ9igPZqfcsoktjPnGAJFjYlU8UrBV0H9T2Q7kZLII4yO/ah7alXxWsG3/vkJxEwNPnM@c3EvGr07il57a/2UbXeiNAiwQNKDuHAdwe6/SUAA8Pf3H0mQBudjGjfTw9Ohuf@8h13@vofa8KG6hTJrTWJ@j1ldF1W1/Qc)
[Answer]
# [Go](https://go.dev), ~~364~~ ~~357~~ 347 bytes
```
import(."math/rand";."regexp")
func f(s string)string{return MustCompile(`(\w)(\w+)(\w)`).ReplaceAllStringFunc(s,func(S string)string{k:=S[1:len(S)-1]
for i:=range k{for _,l:=range[]string{"acemnorsuvwxz","bdfhkl","gpqy","it","j"}{if o,_:=MatchString("["+l+"]",k[i:i+1]);o{q:=Intn(len(l));k=k[:i]+l[q:q+1]+k[i+1:]}}}
return S[:1]+k+S[len(S)-1:]})}
```
[Attempt This Online!](https://ato.pxeger.com/run?1=jVPBitswEKX0lp_oICi1sZuSW_HiwrLQsoctpW5PTtgo9jjWWpYSSV4nG_IlvSyFfkR_pV_TkZ1dltLDQmwpmtGb996Mf_xc6_vfG140fI3QcqEmot1o46asah371bnq7fs_L_LxMJiylrv6neGqZGdTZnCNuw0LJ1WnCqgCC9YZodbhuBwMus4ouOqsu9DtRkgMlsG8D-mJ_CtchtOvuJG8wHMps-HSR4IKbOwRg-wfvCZJs3yWSFRBFr6dLSaVNiCSlPgQ--bg_17H8nSQL07XGOG3Shvb3fa7OxazVVnVjaTNerPd0yIcvW7Y8SAq0PF1kl5xV9Qjn4DlLJIRW7C4yUUiotkiPNOHbZJeKqcCz0WG4VmTNnkiFpHMt8mWciJKjmbJ4ng8Tk42ZHniz6MsfxBA4fB48vjlq8FE34IghMMkS1LmaoRtJ4oGVkb3Ciq9g5uu3WAJ-hYN-Ljkd3so9ZpNvhBdVwXstZ2r9MNc-XWuWAxZTL3JwnCSpey8KLQpSRc4DRwMWuSmqAmMO7jg7cqIkrz8rgQVsMLtYxCO8NGqN47YOUepQkFfUz4hPbBAH7A-wqGnc-AG4yGkldzDOD9cOTryxYWlDXdDQiWMdUAjRVpoM0LBCuEUN2JdOximZArf_AFSWsHVkEM6HJfQorUDxl53Q8w6ISWl8tIL6IWrdUcoRq8kth6HKNBvhQXvLA6F6q6li60gFC8YlHYjAJIX-0dipMZZlBWsupHgqNd65bWWOH1eK5aXzhPokWg2yrd3cIQE4c49kbDCkURf0_cz1BNKcVNa0JVnMpS3UPNbpFyka4WhPkosY09KarKbe7tRmP97bcnc7iHDDxansuSWFIpcMOhnEgqtrG_gFD7RaPg2b7xIToXg3BZCRJ-x9zcG_vEjC0Be1CeLns4eVSPbTSfRJstnGHb6Tu7vx_Uv)
Uses an adaptation of my ["Cambridge Transposition"](https://codegolf.stackexchange.com/a/257251/77309) solution.
* -7 bytes from some reorganization
* -10 bytes from using string slices and general cleanup
[Answer]
# [Python 3.8 (pre-release)](https://docs.python.org/3.8/), 241 bytes
Breaks, if words contain numbers (which is quite unlikely..)
```
import random,re
g=lambda c:sum([(p:="jitgpqybdfhkl".find(c))<0,p<1,p<3,p<7])
def f(m,C=[[],[],[],[],[]]):
 for c in m[0]:l=C[g(c)];l+=[c];random.shuffle(l)
 return''.join(C[g(c)].pop()for c in m[0])
h=lambda s:re.sub('(?<=\w)\w+(?=\w)',f,s)
```
[Try it online!](https://tio.run/##VZFNb9wgEIbv/hUjXxa0lpUqh1bOWlGVQ5RLT705PrAw2OxiIIDX2f757dhtqlQC8TUz7/MO4ZpH7@6/hXi7mSn4mCEKp/xURSyG1orpqATIJs0T61ho2vJk8hDerkelx7Mta22cYpLzw10VDl9o3tP82vNCoQbNpuqp7bq@@jR63hSgfQQJxsHU3fWNbZ@6gar0D3bfdrJ/@MNQp3HW2iKzvICIeY5ut6tP3jj2N74OPjD@XzFejB/YqYlYp/nIduzx0L4u/HXZs8d1s6t0lfgtY8oJWujKlwwmwYLWwtn5xUEeRQYBGd8zSOEgZUNvRyQOoWAZjUWKQVJ1IqoEXoOhWotfD6O4IMUipclILBZVBSKB9W5YV0o0EbSJiTScAitoYzFnjAmCnT8i/AWjIFk/Z2scJhKfBPmU3qUsXK7LCspncyEhASEalwVpwfckjdn/wGVN2ixU/0AAhRw3TBBS0mIIKftVMJG52WJq1qo/ydvbbOQZjnHth/bvcJqnkDaozboVv66g/FCXfVFsfyDS2hDY2kqfvBGxka33nN9@Aw "Python 3.8 (pre-release) – Try It Online")
# [Python 3.8 (pre-release)](https://docs.python.org/3.8/), ~~298~~ ~~281~~ ~~261~~ ~~255~~ 249 bytes
```
import random,re
g=lambda c:sum([(p:="jitgpqybdfhkl".find(c))<0,p<1,p<3,p<7])
def f(m,C=[[],[],[],[],[]]):
 t,*u,v=m[0]
 for c in u:l=C[g(c)];l+=[c];random.shuffle(l)
 return t+''.join(C[g(c)].pop()for c in u)+v
h=lambda s:re.sub('[A-Za-z]{4,}',f,s)
```
[Try it online!](https://tio.run/##TVFNb9swDL37VxC@xF5Uo0MHbHDrQ9HDsMtOO83wgZGpWIksqfpImg777RntrdsAEdQHH997lL@kydm7Tz5cr3r2LiQIaEc3i0DFvjM470YE2cY8V33l26486LT3z5fdqKajKRul7VjJun64Ff7hPccdx8ehLkZSoKpZPHV9P4j/1lC3BSTxLotTN/e3QwHKBZCgLeTWdE/9nvsN92bb9XK4/62miVNWylBl6gICpRwspO1m0xycttUfSOOdr@p/zertqZjeLMQ2UBPzrtr0jzff8eZ1@PFB/NwIJWJ9TRRThA768ksCHeFMxsDRujOzTJgAIdFLAokWYtL8tiNWgSOcJ22Ia4gJLYYxglOgudfZLYcJT8S1xDAZWIehUQBGMM7ul8xAHUDpEJnDjmCQN4ZSohDBm/xW4U4UkGldTkZbikw@I1uUzsaENjWlgPKzPjERgg/aJmQueIxS6@1XOi@g1YL4KwQI5bTKBJSSk2ZJyS2Ekc1lQ7Fdun5jb89ZyyPswjIP5V7gkGcfV1GrdYOvFxjdvimHoljHj3EZCKxj5c9eFVVTtdzX9fUX "Python 3.8 (pre-release) – Try It Online")
[Answer]
## Clojure, ~~326~~ ~~322~~ 324 bytes
Update 1: replaced `(map(fn[[k v]]...)...)` with `(for[[k v]...]...)`
Update 2: fixed regex, using `\pL` instead of `\w` etc.
```
#(let[G(zipmap"bdfhklgpqyitj""0000001111223")](apply str(flatten(interleave(for[v(re-seq #"\pL+"%)w[(rest(butlast v))]W[(into{}(for[[k v](group-by G w)][k(shuffle v)]))]R[(rest(reductions(fn[r i](merge-with + r{(G i)1})){}w))]][(first v)(map(fn[c r](nth(W(G c))(-(r(G c))1)))w R)(if(second v)(last v))])(re-seq #"\PL+"%)))))
```
I'm looking forward to seeing something shorter. Earlier ungolfed version with a few example runs:
```
(def f #(let[G(zipmap"bdfhklgpqyitj""0000001111223")] ; Create groups, the longest "acemnorsuvwxz" goes to an implicit group nil
          (apply str(flatten(interleave
                              (for[v (re-seq #"\w+"%)                                          ; Iterate over words
                                   w [(rest(butlast v))]                                       ; This holds the middle part
                                   W [(into{}(map(fn[[k v]][k(shuffle v)])(group-by G w)))]    ; Create shuffled groups
                                   R [(rest(reductions(fn[r i](merge-with + r{(G i)1})){}w))]] ; Calculate cumulative sum of group items, used to look-up nth value from shuffled values
                               [(first v)                                     ; First character
                                (map(fn[g r](nth(W g)(-(r g)1)))(map G w)R)   ; Shuffled middle part
                                (if(>(count v)1)(last v))])                   ; Last character, unless the word is just a single character
                              (re-seq #"\W+"%)))))) ; Interleave with spaces, commas, newline etc.
(f "It is well known that a text can still be read while the innards of its words have been scrambled, as long as their first and last letters plus their overall outlines remain constant.\n")
;  "It is well known that a txet can sitll be read wlihe the irnands of its wrods hvae been seacmlbrd, as lnog as their fisrt and lsat letters plus their oavrell ontlieus rmaein cnontast.\n"
;  "It is well kwonn that a text can sitll be raed wlihe the innards of its wrods hvae been seramlbcd, as long as their fisrt and lsat lettres plus their oravell ouiltnes rmeain cnsatont.\n"
;  "It is well konwn that a text can still be read while the iarnnds of its words have been sraemlbcd, as lnog as their first and lsat lrttees plus their oaevrll ontlieus remain canntsot.\n"
```
[Answer]
# [Perl 6](http://perl6.org/), ~~241~~ 195 bytes
Includes +1 byte for `-p` command-line switch.
```
s:g/(<:L>)(<:L>+)(<:L>)/{$0}{[~]
$1.comb.pairs.classify({first
.value~~*,:k,/<[bdfhkl]>/,/<[gpqy]>/,/<[it]>/,/j/,!0}).values.map({$_».key
»=>«$_».value.pick(*)})».List.flat.sort».value}$2/;
```
Ungolfed:
```
s:g/(<:L>)(<:L>+)(<:L>)/{$0}{
    [~]
    $1.comb
    .pairs
    .classify({
        first .value ~~ *, :k,
            /<[bdfhkl]>/,
            /<[gpqy]>/,
            /<[it]>/,
            /j/,
            !0
    })
    .values
    .map({ $_».key »=>« $_».value.pick(*) })
    ».List
    .flat
    .sort
    ».value
}$2/;
```
[Answer]
## C#, ~~438~~ ~~394~~ ~~380~~ 374 bytes
```
namespace System.Text.RegularExpressions{using Linq;s=>Regex.Replace(s,@"\p{L}(([gpqy])|(i|t)|(j)|([bdf-l])|([a-z]))*?[a-z]?\b",m=>{var a=m.Value.ToArray();for(int i=1,j;++i<7;){var c=m.Groups[i].Captures;var n=c.Cast<Capture>().Select(p=>p.Index-m.Index).ToList();foreach(Capture p in c){a[j=n[new Random().Next(n.Count)]]=p.Value[0];n.Remove(j);}}return new string(a);});}
```
*Save 10 bytes thanks to @MartinEnder♦.*
Annoyingly, [`CaptureCollection` doesn't implement `IEnumerable<T>`](https://stackoverflow.com/questions/19035527/why-can-i-not-call-select-from-a-capturecollection-object) and that's why the `.Cast<Capture>()` is needed. Hopefully, I can combine the Linq query and the `foreach` loop though.
I'm sure there's a lot that can be golfed but it took me long enough just to get it working...
[Try it online!](https://tio.run/nexus/cs-mono#fVNNb9swDL37VxA5SWtibJddnKYrgm0Y0A1DUmxAXR8Um4mV2ZIryYnTLr89o@1kazO7vJjmx3uPknhQIkdbiBhhvrMOc@/JA7LSSrWCW6ycP8NVmQnzsSoMWiu1ssGzkhupHgKvCcSZsBa@N36LUpt1wskYNlom8FVIxfjf1L@i2j6VKh5bZwh1CO13Aku4BAuXkxeVtZEqrEhbkZF2ZofwYRBej@7E6DFiLKRYrrSx5ah6jPhvFi6SZfora9xV8bBrHOkizt9chXXP1f1iMIS8i@npv0htG2FAkLjc/yGyEv1bPU2FuTZG7Bg/Hsi5LbUBJpUDSY3vhrAO4OJCwhjeB7yzoZv6RB839J@NLgsbysifisKVdEc97Kc2RW0xFVs3PnZMGPfnmGHsWEEnAIX/RSVYwYjgG4/TeDfSunqyPujeBE2NIk7ZkQwKkApi3lvfP3RtIlyTfhUq3MJMqETnJP4bvVOm/KkuleNRRAVFey3h2yh4FU7RE8r1Btn6ldH2Xne0M2yQplRQ62sfMRMd0Puz2IufKe2YztD/aaRDWjBkSzaYx0bkiwxhq01iYZtK8uuVRLOpF9GlKA3o0mXUYAf8jOCEOUORNJDP8u18e29/OPwB "C# (Mono) – TIO Nexus")
Formatted/Full version:
```
namespace System.Text.RegularExpressions
{
    using Linq;
    class P
    {
        static void Main()
        {
            Func<string, string> f = s =>
                Regex.Replace(s, @"\p{L}(([gpqy])|(i|t)|(j)|([bdf-l])|([a-z]))*?[a-z]?\b", m =>
                {
                    var a = m.Value.ToArray();
                    for (int i = 1, j; ++i < 7;)
                    {
                        var c = m.Groups[i].Captures;
                        var n = c.Cast<Capture>().Select(p => p.Index - m.Index).ToList();
                        foreach(Capture p in c)
                        {
                            a[j = n[new Random().Next(n.Count)]] = p.Value[0];
                            n.Remove(j);
                        }
                    }
                    return new string(a);
                });
            Console.WriteLine(f("Scramble words while preserving their outlines"));
            Console.ReadLine();
        }
    }
}
```
[Answer]
# [Perl 5](https://www.perl.org/) `-p`, 104 bytes
```
s|\w+|$t=$&;map{@a=$t=~/\B[$_]\B/g;$t=~s/\B[$_]\B/splice@a,rand@a,1/ge}'^bdf-lqpty',bdfhkl,gqpy,it;$t|ge
```
[Try it online!](https://tio.run/##dVJLbxMxEL7vr5hDREHdJO2hF6pKTS8IiccBJCQIrSbeydqqYzu2N9uIwE9n@ZymyYnTjO2Z77UbJNqrYUi7eX@@G@Wb0avrFYdft3yDw5/p/O7H6OHn/G7aXpdzOl2kYI2SW64juwblctrK77P7RbMc23XI27MarX60dbsO29pk7O9aGYYvKvJqYYV6H5tEvTboQ5QkcWNcS1mLieS7bI2TVFXvMxmMibX06HzvMMCZmLI8ZVLsKGWDt4VQFG4OeAAh4xwXBr8kk9OBTvNGMCtYO@hoauJE1oMa9Zl9aWICh2vIMhorOUtMFGz3MuE3Ehm0LzpBvmLjSHmXMrs8oXdmAxaGNeMyF8OzpIw5/yR92djrr48qSFjpvUZipVD2UfjCluCss5LeVtUhu/K26iAMpl@HJF3j31D5DH41qarZMwxCY0qy7sQpKSGUTD5wLiI1R1bFUk0z3EfftZq@Y/ezs1sEZ7Jhe7Tdl3wFhgtfF4JExUkw/RWI/4lqLw9JbKlz2XdKS4OFb/oUPEzU5AvfiSdrqPPuKHfprfV9sdtCYwAsAOUJ@l2Ln8ayQix/fcgGsQ/jj1eTi8uLYRz@AQ "Perl 5 – Try It Online")
]