Wygeneruj losowy UUID

15

Potrzebuję UUID. Twoim zadaniem jest wygenerowanie jednego.

Kanoniczny UUID (Universally Unique IDentifier) ​​to 32-cyfrowy numer szesnastkowy z myślnikami wstawionymi w niektórych punktach. Program powinien wypisywać 32 cyfry szesnastkowe (128 bitów) w postaci xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx( 8-4-4-4-12cyfr), gdzie xjest losową liczbą szesnastkową. Zakładając, że PRNG w Twoim języku jest doskonały, wszystkie prawidłowe dane wyjściowe muszą mieć takie samo prawdopodobieństwo wygenerowania.

TL; DR

Wygeneruj 32 losowe cyfry szesnastkowe w postaci 8-4-4-4-12cyfr. Najkrótszy kod wygrywa.

EDYCJA: Musi być szesnastkowy. Zawsze generowanie tylko miejsc po przecinku jest nieprawidłowe. EDYCJA 2: Brak wbudowanych. To nie są identyfikatory GUID, tylko ogólne cyfry szesnastkowe.


Przykładowe dane wyjściowe:

ab13901d-5e93-1c7d-49c7-f1d67ef09198
7f7314ca-3504-3860-236b-cface7891277
dbf88932-70c7-9ae7-b9a4-f3df1740fc9c
c3f5e449-6d8c-afe3-acc9-47ef50e7e7ae
e9a77b51-6e20-79bd-3ee9-1566a95d9ef7
7b10e43c-3c57-48ed-a72a-f2b838d8374b

Dane wejściowe i standardowe luki są niedozwolone.


To jest , więc wygrywa najkrótszy kod. Prosimy również o wyjaśnienia.

klaskać
źródło
5
Wygląda na mniej surową wersję codegolf.stackexchange.com/q/32309/14215
Geobits
9
„Te przykłady nie są przypadkowe. Spróbuj nadać im jakieś znaczenie”. Co to znaczy?
Alex A.
3
W rzeczywistości nie potrzeba liczb szesnastkowych, 10-podstawowa może być również losowa. Na przykład, czy 12345678-1234-1234-1234-123456789012powinien być prawidłowym UUID (czy jest potrzebna jakaś cyfra szesnastkowa?). Czy uważasz to za lukę?
Voitcus,
3
Tytuł i pierwsze zdanie sugerują, że chcesz mieć kanoniczny identyfikator UUID, a podane przykłady wydają się być zgodne ze specyfikacją dla UUID, ale wydaje się, że faktycznie pytasz o coś innego.
Peter Taylor,
3
Czuję się zmuszony wskazać, że UUID w wersji 4 (losowy) ma wymagany format, xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx gdzie yjest jeden [89AB]. W momencie tego komentarza nie ma gwarancji, że żadna z odpowiedzi (oprócz C # przy użyciu wbudowanej biblioteki) wygeneruje prawidłowy losowy identyfikator UUID (i faktycznie prawdopodobnie nie wygeneruje żadnego).

Odpowiedzi:

11

Pyth, 20 bajtów

j\-msm.HO16*4hdj83 3

Demonstracja.

Koduje [1, 0, 0, 0, 2]jako 83 w bazie 3, a następnie dodaje jeden i mnoży przez cztery, aby uzyskać długość każdego segmentu. Następnie tworzy cyfry szesnastkowe i łączy na łącznikach.

isaacg
źródło
8

Julia, 80 bajtów

h=hex(rand(Uint128),32)
print(h[1:8]"-"h[9:12]"-"h[13:16]"-"h[17:20]"-"h[21:32])

Wygeneruj losową 128-bitową liczbę całkowitą, uzyskaj jej szesnastkową reprezentację jako ciąg dopełniany do 32 cyfr i podziel ją na segmenty połączone myślnikami.

Dzięki ConfusedMr_C i kvill za pomoc!

Alex A.
źródło
8

CJam, 26 25 bajtów

8 4__C]{{Gmr"%x"e%}*'-}/;

Wypróbuj online w interpretatorze CJam .

Jak to działa

8 4__C]{              }/   For each I in [8 4 4 4 12]:
        {         }*         Do I times:
         Gmr                   Pseudo-randomly select an integer between 0 and 15.
            "%x"e%             Apply hexadecimal string formatting.
                    '-       Push a hyphen-minus.
                        ;  Discard the last hyphen-minus.
Dennis
źródło
5

PowerShell, 77 69 67 bajtów

((8,4,4,4,12)|%{((1..$_)|%{'{0:X}'-f(random(16))})-Join""})-Join"-"

edycja: obce pareny:

((8,4,4,4,12)|%{((1..$_)|%{('{0:X}'-f(random(16)))})-Join""})-Join"-"

edit: udało się usunąć końcowe .Trim („-”) z oryginału:

(((8,4,4,4,12)|%{((1..$_)|%{('{0:X}'-f(random(16)))})+"-"})-Join"").Trim("-")

Może być wyraźniejszy z pewnymi białymi znakami, biorąc pod uwagę naturę flag (-f i -Join). Nadal chciałbym zgubić ostateczne przycięcie („-”):

(((8,4,4,4,12)|%{((1..$_)|%{('{0:X}' -f (random(16)))}) + "-"}) -Join "").Trim("-")

Lub przy użyciu wbudowanej funkcjonalności (ala odpowiedź C # powyżej)

'{0}'-f[System.Guid]::NewGuid()

Wydaje się to jednak trochę skrót-y, nawet jeśli ma 31 bajtów.

Czterdzieści3
źródło
61 bajtów:(8,4,4,4,12|%{-join(1..$_|%{'{0:X}'-f(random(16))})})-join'-'
mazzy
5

Python 2, 86 84 bajtów

from random import*;print'-'.join('%%0%ix'%i%randint(0,16**i-1)for i in[8,4,4,4,12])

Łańcuch formatuje formaty ciągów, aby Python sformatował liczby szesnastkowe niepowtarzalnie dla każdego segmentu.

Nie golfowany:

import random

final = []
for i in [8, 4, 4, 4, 12]:               # Iterate through every segment
    max = (16 ** i) - 1                  # This is the largest number that can be
                                         # represented in i hex digits
    number = random.randint(0, max)      # Choose our random segment
    format_string = '%0' + str(i) + 'x'  # Build a format string to pad it with zeroes
    final.append(format_string % number) # Add it to the list

print '-'.join(final)                    # Join every segment with a hyphen and print

Przydałoby się to trochę ulepszenia, ale jestem dumny.

jqblz
źródło
5

Perl 5 , 43 bajtów

Zaoszczędź 2 bajty dzięki @Xcali !

printf"%04x"."-"x/[2-5]/,rand 2**16for 1..8

Wypróbuj online!

Dom Hastings
źródło
1
Ogolono dwa bajty, wciąż przy użyciu podstawowej metody: Wypróbuj online!
Xcali,
Dziękuję @Xcali!
Dom Hastings,
4

PHP, 69 72 75 bajtów

foreach([8,4,4,4,12]as$c)$r[]=rand(".1e$c","1e$c");echo join('-',$r);

Nie wyświetla to cyfr szesnastkowych ( a, ... f). Są dozwolone, ale nie są wymagane przez treść pytania.

Żadna grupa cyfr nie zaczyna się od 0(również nie jest wymagana).

edycja: zapisane 3 bajty dzięki @IsmaelMiguel

Voitcus
źródło
To wygląda jak bi więcej niż 32 bajty.
isaacg
@isaacg tak, przepraszam - mój błąd
Voitcus
Zamiast tego powinieneś użyć join().
Ismael Miguel
3

C #, 65 bajtów

using System;class C{void Main(){Console.Write(Guid.NewGuid());}}

edycja: Tak! C # jest krótszy niż inny język (oprócz Java) :)

Stephan Schinkel
źródło
1
Myślę, że jest to uważane za standardową lukę ... :( meta.codegolf.stackexchange.com/questions/1061/…
Dom Hastings
1
Myślę, że nie jest to uważane za standardową lukę: jak widać, prośba o porzucenie tych rzeczy dostała tylko 2 głosy poparcia w ciągu 1 roku. Przeciwnie, komentarz mówiący o tym, że powinieneś używać wbudowanych funkcji, otrzymał 58 pozytywnych opinii. Lub, jak powiedział jeden z komentatorów -> Gdybyśmy wszyscy byli ograniczeni do tego samego zestawu wbudowanych funkcji, każdy konkurs byłby wygrany przez APL lub Golfscript, ponieważ ich nazwy poleceń są najkrótsze. (Michael Stern)
Stephan Schinkel
1
lub po prostu odwrotnie: czy możemy użyć printf? czy powinniśmy użyć wbudowanego asm, aby wywołać przerwanie 21?
Stephan Schinkel
Dobra uwaga! Nie chciałem się denerwować, chciałem tylko pomóc! Myślę, że Mathematica mogłaby wygrać CreateUUID[]!
Dom Hastings,
1
@StephanSchinkel „Tylko 2 głosy poparcia w ciągu roku” są mylące. Ma obecnie 47 głosów pozytywnych i 45 głosów negatywnych, więc netto +2. To powiedziawszy, ogólnie przyjęty próg jest wyższy, więc masz rację, że „tak naprawdę” nie liczy się teraz jako standardowa luka.
Geobits
3

gawk, 86

BEGIN{for(srand();j++<32;printf(j~"^9|13|17|21"?"-":E)"%c",x+(x>10?87:48))x=rand()*16}

Możesz użyć tego raz na sekundę, aby wygenerować unikalny losowy „UUID”. Jest tak, ponieważ srand()używa czasu systemowego w sekundach od epoki jako argumentu, jeśli nie podano argumentu.

for n in `seq 100` do awk 'BEGIN{for(srand();j++<32;printf(j~"^9|13|17|21"?"-":E)"%c",x+(x>10?87:48))x=rand()*16}'; sleep 1; done

Myślę, że część awk jest raczej elegancka.

BEGIN{
    srand()
    for(;j++<32;) {
        x=rand()*16
        x+=(x>10?87:48)
        printf "%c", x
        if(j~"^8|12|16|20")printf "-"
    }
}

Jeśli chcesz go używać częściej niż raz na sekundę, możesz wywołać go w skrócie w ten sposób. Zauważ, że część awk również się zmieniła.

echo `awk 'BEGIN{for(srand('$RANDOM');j++<32;printf(j~"^9|13|17|21"?"-":E)"%c",x+(x>10?87:48))x=rand()*16}'`

echoDodaje tam wydrukować nową linię za każdym razem.

Cabbie407
źródło
3

K5, 35 bajtów

"-"/(0,8+4*!4)_32?`c$(48+!10),65+!6

Aby wygenerować alfabet szesnastkowy, generuję ciąg znaków ( `c$) z listy cyfr ( 48+!10) i pierwszych 6 wielkich liter ( 65+!6). Alternatywnym sposobem generowania cyfr o tej samej długości jest ,/$!10.

Po wygenerowaniu ciągu „0123456789ABCDEF” reszta jest prosta. Wybierz 32 losowe wartości z tego zestawu ( 32?), pokrój ( _) wynikowy ciąg przy 0 8 12 16 20obliczeniu za pomocą (0,8+4*!4), a następnie połącz powstałe fragmenty ciągu za pomocą myślników ( "-"/).

W akcji:

  "-"/(0,8+4*!4)_32?`c$(48+!10),65+!6
"9550E114-A8DA-9533-1B67-5E1857F355E1"
JohnE
źródło
3

R , 63 bajty

x=sample(c(0:9,letters[1:6]),36,1);x[0:3*5+9]='-';cat(x,sep='')

Wypróbuj online!

Kod najpierw tworzy losowy ciąg 36 znaków, a następnie umieszcza cztery łączniki. Wysyła UUID na standardowe wyjście.

Nie dotyczy
źródło
Zamień cpołączenie sprintf("%x",0:15)na -1.
J.Doe,
3

JavaScript, ES6, 106 bajtów

"8-4-4-4-12".replace(/\d+/g, m => {t=0;for(i=0; i<m; i++) {t+=(Math.random()*16|0).toString(16)}return t})

Używa zamiany Regex. Traktuje ciąg formatu jako liczbę do wygenerowania znaku szesnastkowego. Podnoszenie gdziekolwiek mogę; w miarę możliwości pomijając średniki.

Reno McKenzie
źródło
89 bajtów,'8-4-4-4-12'.replace(/\d+/g,n=>Math.floor(16**n*Math.random()).toString(16).padStart(n,0))
kamoroso94,
2

Perl 6 , 53 bajtów

Oczywisty:

say join '-',(0..9,'a'..'f').flat.roll(32).rotor(8,4,4,4,12)».join # 67

Tłumaczenie przykładu Perla 5 przy użyciu printfpowoduje, że kod jest nieco krótszy.

printf ($_='%04x')~"$_-"x 4~$_ x 3,(0..^4⁸).roll(8) # 53
Brad Gilbert b2gills
źródło
(0..16⁴)?! Możesz to zrobić w Perlu?
klaszcz
1
@VoteToSpam Możesz od 9 dni temu . (Perl 6 zostanie wydany później w tym miesiącu)
Brad Gilbert b2gills
Coire. Może powinienem się tego nauczyć
klaszcz
@VoteToSpam To nic w porównaniu do tego, 1,2,4,8,16 ... *co generuje leniwą nieskończoną listę mocy 2. ( {2**$++} ... *działa również)
Brad Gilbert b2gills
2

Kotlin , 175 bajtów

fun main(a:Array<String>){
fun f()="0123456789abcdef".get((Math.random()*16).toInt())
var s=""
for(i in listOf(8,4,4,4,12)){
for(j in 1..i)
s+=f()
if(i!=12)s+="-"}
println(s)}

Wypróbuj online!

Mój pierwszy program Kotlin i zgłoszenie PPCG

Cheldon
źródło
152 bajty -> tio.run/…
jrtapsell
2

APL (Dyalog Unicode) , 115 78 bajtów

a←⊣,'-',⊢
H←⊃∘(⎕D,819⌶⎕A16∘⊥⍣¯1
(H 8?16)a(H 4?16)a(H 4?16)a(H 4?16)a H 12?16

Wypróbuj online!

To jest moje pierwsze zgłoszenie APL. Ogromne podziękowania dla @ Adám za spotkanie ze mną na czacie APL PPCG i za funkcję konwersji szesnastkowej.

Dzięki @ Zacharý za 1 bajt

Edytowane, aby naprawić liczbę bajtów.

J. Sallé
źródło
Możesz założyć ⎕IO←0bez bajtów, Adám to robi. Ponadto większość bajtów (IIRC, wszystkie te, które masz tutaj) można policzyć jako jeden w APL.
Zacharý
@ Zacharý Użyłem TIO do zliczenia bajtów dla mojego przesłania, czy zamiast tego powinienem był użyć liczby znaków? Wciąż jestem nowy w PPCG i korzystam z APL, więc nie mam zbyt dużej wiedzy na temat tego, jak to zrobić.
J. Sallé,
Możesz także zmienić a(H 12?16)na, a H 12?16aby zapisać jeden bajt.
Zacharý
2

Japt , 32 bajty

[8,4,4,4,12]m@MqG**X sG ù0X} q"-

Wypróbuj online!

Bejofo
źródło
Witaj w PPCG i witaj w Japt :) Omówię twoje rozwiązania do tej pory, kiedy będę mógł poświęcić trochę czasu (właśnie wracam z wakacji, tyle do nadrobienia), ale pierwszą wskazówką, którą zaoferuję, jest zapoznanie się skorzystaj ze skrótów Unicode ( m@- £na przykład), a aby pomóc Ci zacząć, przygotuj 24-bajtową wersję swojego rozwiązania: ethproductions.github.io/japt/… Jeśli masz jakieś pytania, wejdź do pokoju rozmów Japt .
Kudłaty
1

MATLAB / oktawa, 95 bajtów

a='-';b=strcat(dec2hex(randi(16,32,1)-1)');[b(1:8) a b(9:12) a b(13:16) a b(17:20) a b(21:32)]
costrom
źródło
1

Perl , 51 bajtów

say"xx-x-x-x-xxx"=~s/x/sprintf"%04x",rand 65536/reg

Wymaga perl5> = 5,10 Myślę, że. Dla modyfikatora / r i dla powiedzmy ().

Kjetil S.
źródło
1
Ładny! To o wiele lepsze niż moje! Po zapoznaniu się z rozwiązaniem możesz nawet zaoszczędzić więcej na podstawie tego meta postu przy s//xx-x-x-x-xxx/;s/x/sprintf"%04x",rand 65536/egużyciu -pflagi, co oznaczałoby również, że działa on na starszych wersjach bez -E.
Dom Hastings,
Dzięki. Twoja sugestia to: echo | perl -pe's // xx-xxx-xxx /; s / x / sprintf "% 04x", rand 65536 / np. „A to tylko 48 znaków między”. (Czy to jest oszustwo? Może nie)
Kjetil S.,
Zgodnie z tym meta postem jest to do przyjęcia, nie miałem jeszcze okazji sam skorzystać z tego mechanizmu, ale mam nadzieję, że wkrótce to zrobię! Byłoby 49 bajtów (+ -p), ale nadal całkiem niezłe i nie rozważyłbym tego podejścia bez zobaczenia twojej odpowiedzi!
Dom Hastings,
1

C ++, 194 193 221 210 201 bajtów

+7 bajtów dzięki Zacharýowi (wykryto, -że nie powinno być na końcu)

#include<iostream>
#include<random>
#include<ctime>
#define L(a)for(int i=0;i<a;++i)std::cout<<"0123456789abcdef"[rand()%16];
#define P(a)printf("-");L(a)
void t(){srand(time(0));L(8)P(4)P(4)P(4)P(12)}

Jeśli ktoś ma sposób na uzyskanie innej wartości przy każdym wykonaniu bez zmiany srandi bez uwzględnienia <ctime>, byłoby świetnie

HatsuPointerKun
źródło
Nie może #define L(a) for... być #define L(a)for...? (
Być
To jest nieważne, na końcu jest „-” (którego nie powinno być)
Zacharý
@ Zacharý Correction zastosowano teraz
HatsuPointerKun
210 bajtów
Zacharý
1
Czy możesz zrobić coś takiego "0123456789abcdef"[rand()%16], a następnie usunąć f?
Zacharý
1

Befunge-93 , 97 bajtów

v>4448v,+<    <
0*    :  >59*0^
62v0-1_$:|>*6+^
>^>41v < @^99<
v<*2\_$:54+` |
?0>+\1-^ v*68<>
>1^

Wypróbuj online!

Jestem pewien, że można to zmniejszyć, ale to moja pierwsza próba :)

nornagon
źródło
1

Bash, 67 bajtów

for l in 4 2 2 2 6;{ o+=`xxd -p -l$l</dev/random`-;}
echo ${o::-1}
apilat
źródło
Witamy w PPCG!
Dennis
1

JavaScript REPL, 79 bajtów

'66-6-6-6-666'.replace(/6/g,_=>(Math.random().toString(16)+'00000').slice(2,6))

Wypróbuj online!

Math.randommoże wrócić 0. Dodanie 5 zer powoduje, że krojenie dostaje 4 0s

l4m2
źródło
1

Dalej (gforth) , 91 89 bajtów

include random.fs
hex
: f 0 4 4 4 8 20 0 do dup i = if + ." -" then 10 random 1 .r loop ;

Wypróbuj online!

Wyjaśnienie

Zmienia podstawę na szesnastkową, a następnie wyprowadza liczby / segmenty o odpowiedniej długości z myślnikami w określonych odstępach czasu

Objaśnienie kodu

include random.fs          \ include the random module
hex                        \ set the base to hexadecimal
: f                        \ start a new word definition
  0 4 4 4 8                \ enter the intervals to place dashes
  20 0 do                  \ start a counted loop from 0 to 0x20 (32 in decimal)
    dup i =                \ check if we are on a character that needs a dash
    if                     \ if we are
      +                    \ calculate the next character that gets a dash
      ." -"                \ output a dash
    then                   \ end the if block
    f random               \ get a random number between 0x0 and 0xf
    1 .r                   \ output it right-aligned in 1-character space
  loop                     \ end the loop
;                          \ end the word definition
reffu
źródło
1

C (gcc) ,  94   91  86 bajtów

main(i){srand(&i);i=803912;for(;i--%16||(i/=16)&&printf("-");printf("%x",rand()%16));}

Wypróbuj online!

Chciałbym zasugerować tę wersję w komentarzu do Maxa Yekhlakova ( jego odpowiedź ), ale niestety nie mam jeszcze 50 potrzebnych punktów reputacji, więc oto moja odpowiedź.

803912jest C4448zapisany w systemie szesnastkowym, opisuje sposób sformatowania danych wyjściowych ( 12-4-4-4-8), jest odwrócony, ponieważ najpierw zostaną odczytane najmniej znaczące cyfry.
 

Edycje:

  • zaoszczędził 3 bajty dzięki Jonathanowi Frechowi
  • zaoszczędził 5 bajtów, zastępując srand(time(0))jesrand(&i)
Annyo
źródło
1
main(){...;int i= can be main(i){...;i=.
Jonathan Frech
I've been thinking something, apparently srand() accept an unsigned int as its seed parameter. On tio.run, an unsigned int is 4 bytes long but the UUID is 16 bytes long. This means only a tiny fraction of the valid outputs (1/2^12) will be generated, thus my solution (as well as the previous one with time(0)) is not valid. What do you think ?
Annyo
The OP states Assuming that your language's PRNG is perfect, all valid outputs must have the same probability of being generated.. The seed entropy does not necessarily determine the RNG entropy, though it likely does (did not check the srand() implementation). However, srand() is to my knowledge reasonably uniform, so if the RNG was perfect, it would still be uniform. I therefore think your answer is valid.
Jonathan Frech
Ok, I understand. I could also submit my answer as a function, assuming srand() has already been done, and in this case there will be no doubt. But I'm not sure if this is allowed, other C/C++ submissions all seem to include srand() int the answer (unless it does not use rand())
Annyo
81 bytes
ceilingcat
1

C (gcc), 143 110 103 96 94 bytes

Golfed down to 94 bytes thanks to ceilingcat and Jonathan Frech.

(*P)()="\xf\x31À";*z=L"\10\4\4\4\14";main(n){for(;*z;*++z&amp;&amp;putchar(45))for(n=*z;n--;printf("%x",P()&amp;15));}

Try it online!

Explanation:

/*
  P is a pointer to a function.
  The string literal contains actual machine code of the function:

  0F 31     rdtsc
  C3        ret

  0xc3 is the first byte of the UTF-8 representation of the character À
*/
(*P)() = "\xf\61À";

// encode uuid chunk lengths as literal characters
// we use wide characters with 'L' prefix because
// sizeof(wchar_t)==sizeof(int) for 64-bit gcc C on TIO
// so z is actually a zero-terminated string of ints
*z = L"\8\4\4\4\14"

main (n)
{
    for (
        ; 

        // loop until we reach the trailing zero
        *z;

        // increase the pointer and dereference it
        *++z 
             // and output a hyphen, if the pointer does not point at zero
             && putchar(45) 
    )
        // output a random hex string with length pointed at by z
        for (n = *z; n--; printf ("%x", P()&15));
}
Max Yekhlakov
źródło
1
Hello and welcome to PPCG! 110 bytes.
Jonathan Frech
@JonathanFrech Thank you! Your version is very impressive!
Max Yekhlakov
Suggest *z=L"\27\23\17\vz" instead of *z=L"\10\4\4\4\14" and for(n=32;n--;z+=printf("-%x"+(n!=*z),P()&15)-1) instead of for(;*z;*++z&&putchar(45))for(n=*z;n--;printf("%x",P()&15))
ceilingcat
1

Java with Ten Foot Laser Pole v. 1.06, 126 bytes

String u(){return sj224.tflp.util.StringUtil.replace("aa-a-a-a-aaa","a",s->String.format("%04x",(int)(Math.random()*65536)));}

Tested with version 1.06 of the library, but this should work with any version 1.04 or newer.

SuperJedi224
źródło
0

SmileBASIC, 65 62 bytes

DEF G H?"-";:END
DEF H?HEX$(RND(65536),4);
END H G G G G H H H

I created a function to print 4 random hex digits: DEF H?HEX$(RND(65536),4);:END as well as 4 digits with a - after them: DEF G:H?"-";:END. Then it just has to call these functions a bunch of times.

12Me21
źródło
0

Chip, 109 + 6 = 115 bytes

Requires flags -wc36, causing +6 bytes

!ZZZZZZZZZZZZZZZZZZZZZZ
,-----.,+vv--^----^---z
?]]]--R\acd
?xx+-)\\b
?x+x-)\\c
?^xx\--\d
`-xx]v~\e
f*`)'`-\g

Try it online!

Generates 4 random bits (the four ?'s) and converts to hex digits:

  • 0x0 - 0x9 => 0 - 9
  • 0xa - 0xe => b - f
  • 0xf => a

...a bit unconventional, but it saved me some bytes at no expense to the distribution of outcomes.

Phlarx
źródło