Esto eventualmente se detendrá ...

41

Dada una cadena de entrada S, imprima Sseguido de un separador no vacío de la siguiente manera:

  • Paso 1: Stiene la 1/2posibilidad de imprimirse y la 1/2posibilidad de que el programa finalice.

  • Paso 2: Stiene la 2/3posibilidad de imprimirse y la 1/3posibilidad de que el programa finalice.

  • Paso 3: Stiene la 3/4posibilidad de imprimirse y la 1/4posibilidad de que el programa finalice.

  • ...

  • Paso n: Stiene la n/(n+1)posibilidad de imprimirse y la 1/(n+1)posibilidad de que el programa finalice.

Notas

  • La cadena de entrada solo consistirá en caracteres que sean aceptables en el tipo de cadena de su idioma.

  • Se puede usar cualquier separador no vacío, siempre que sea siempre el mismo. Se espera que el separador se imprima después de la última impresión Santes de que finalice el programa.

  • El programa tiene la 1/2posibilidad de finalizar antes de imprimir cualquier cosa.

  • Una nueva línea final es aceptable.

  • Su respuesta debe hacer un intento genuino de respetar las probabilidades descritas. Obviamente, cuando nes grande, esto será cada vez menos cierto. Una explicación adecuada de cómo se calculan las probabilidades en su respuesta (y por qué respetan las especificaciones, sin tener en cuenta los problemas de pseudoaleatoriedad y números grandes) es suficiente.

Tanteo

Este es el , por lo que gana la respuesta más corta en bytes.

Fatalizar
fuente
¿Puede el separador ser una cadena vacía?
rturnbull
16
@rturnbull Bueno, no, porque en ese caso no hay separador.
Fatalize
¿Tenemos que imprimir estos uno después del otro, o podemos imprimirlos todos cuando finalice el programa?
Dennis
@Dennis Uno tras otro.
Fatalize

Respuestas:

18

Pyth , 7 bytes

WOh=hZQ

Pruébalo en línea!

Cómo funciona

Pseudocódigo:

while rand_int_below(1 + (Z += 1)):
    print(input)
Monja permeable
fuente
Pyth vence a 05AB1E de nuevo, ¿no?
Erik the Outgolfer
¿La declaración de impresión no necesita su propia probabilidad junto con la probabilidad de terminación?
tuskiomi
1
@tuskiomi Nah, n / (n + 1) es solo 1-1 / (n + 1), o 1 - (probabilidad de terminación).
Adowrath
29

C #, 94 85 bytes

Mi primera respuesta!

using System;s=>{var r=new Random();for(var i=2;r.Next(i++)>0;)Console.Write(s+" ");}

Intento anterior (me gustó eso goto):

using System;s=>{var i=2;var r=new Random();a:if(r.Next(i++)>0){Console.Write(s+" ");goto a;}}

Sin golf:

using System;
class P
{
    static void Main()
    {
        Action<string> f = s =>
        {
            var r = new Random();
            for (var i = 2; r.Next(i++) > 0;) Console.Write(s + " ");
        };

        f("test");

        Console.ReadKey();
    }
}

Nota: en C #, el Random.Next(N)método devuelve un entero no negativo en el rango [0, N-1], por lo que podemos verificar que el número devuelto sea mayor que 0.

Charlie
fuente
1
Debe incluir using System;en su recuento de bytes. Se puede declarar ren línea, no hay necesidad de ajustar a una variable: new Random().Next(i++). No necesita el punto y coma final en la función de golf.
TheLethalCoder
1
Ah, y buena primera respuesta!
Hubiera
@TheLethalCoder gracias por tus comentarios! Traté de usarlo, new Random().Next(i++)pero cuando intenté ejecutarlo, el resultado siempre fue que el programa se detiene sin imprimir nada o que el programa nunca se detiene. Cuando declaro r=new Random()y uso la rvariable, el programa se detiene más aleatoriamente cuando el OP pregunta.
Charlie
Ahhh prueba porque el lazo es muy apretado.
TheLethalCoder
2
@TheLethalCoder: Sí, el bucle thight significa una posibilidad de que la semilla del generador sea la misma. Ver: msdn.microsoft.com/en-us/library/system.random.aspx#Instantiate
Erno
12

R, 47 46 43 bytes

43 bytes debido a Robin Ryder en los comentarios.

s=scan(,"")
while(sample(T<-T+1)-1)print(s)

Pruébalo en línea!

Explicación

s=scan(,"")  # Takes input from stdin.
             T<-T+1    # T is 1 by default, so this
                       # evaluates to 2, and will increment
                       # at each step.
      sample(T<-T+1)   # Take a sample of size 2, i.e. generate
                       # a list of integers from 1 to 2 in random order
      sample(T<-T+1)-1 # Subtract one from every element of this list.
while(sample(T<-T+1)-1)# while() will treat the first value in this list
                       # as a logical value, i.e. FALSE for zero and TRUE
                       # for nonzero values. The other elements of the list
                       # are ignored, triggering a warning.
                       print(s) # print s
rturnbull
fuente
¿Esto termina alguna vez?
mfloren
@mfloren Sí, como todas las otras respuestas aquí, es estocástico, con una probabilidad de finalización que disminuye a medida que avanza, pero eventualmente terminará. ¡Existe la posibilidad de .5 de que no imprima nada! Intente ejecutarlo varias veces y compare las salidas.
rturnbull
function(s)es más corto ques=scan(,'');
JAD
1
Y pryr::f(while(runif(1)<T/(T<-T+1))print(s))es aún más corto.
JAD
1
@JarkoDubbeldam Desafortunadamente no puede (ab) usar Ty Fcon funciones anónimas, ya que modifica una variable global y significa que la función solo se puede llamar una vez. Vea aquí : "la función de solución funciona de manera consistente independientemente de cuántas veces se haya llamado anteriormente".
rturnbull
11

05AB1E , 8 bytes

[NÌL.R#,

Pruébalo en línea!

Explicación

[         # start loop
 NÌL      # push range [1 ... current_iteration+2]
    .R    # pick a random number
      #   # if true (1), exit loop
       ,  # print input
Emigna
fuente
@Fatalize: Lo hace por mí. Intenta ejecutarlo algunas veces. Tiene un 50% de posibilidades de no generar nada, por lo que es posible que haya tenido "mala suerte".
Emigna
11
El problema heredado de tareas aleatorias. A veces todas las probabilidades están en tu contra.
J_F_B_M
@J_F_B_M inherente?
Leaky Nun
1
@LeakyNun No, es el "Problema de herencia" (la probabilidad de eventos no se hereda de eventos anteriores). J_F_B_M se refería claramente a la Falacia del jugador.
aebabis
11

Javascript, 60 58 54 bytes

f=(s,n=1)=>Math.random()<n/++n?console.log(s)+f(s,n):0

Producirá una cadena s. El separador que se imprime si el programa termina es NaNo 0.

f=(s,n=1)=>Math.random()<n/++n?console.log(s)+f(s,n):0

f('test')

Math.random()devuelve un valor entre 0 y 1. Si ese valor está por debajo n/(n+1), sse imprimirá.

4 bytes guardados gracias a @Neil

Thomas W
fuente
1
¿Por qué no usar n/++n?
Neil
1
@Neil gracias, ¡ahorré 4 bytes!
Thomas W
2
Si su entorno era un navegador que podría usar en alertlugar de console.logguardar 6 bytes, el fragmento podría configurarse alert = console.logpara mostrar una salida no molesta si lo desea (si está permitido, no guarda bytes, solo ayuda a mantener la cordura)
Craig Ayre
10

Java 8, 72 62 61 bytes

s->{for(int n=2;Math.random()<1f/n++;System.out.println(s));}

-10 bytes gracias a @cliffroot .
-1 byte gracias a @JollyJoker .

Delimiter es una nueva línea.

Explicación:

Pruébalo aquí

s->{                          // Method with String parameter and no return-type
  for(                        //  Loop
    int n=2;                  //   Start `n` on 2
    Math.random()<1f/n++;     //   Continue loop as long as a random decimal (0.0-1.0)
                              //   is smaller than 1/`n` (and increase `n` by 1 afterwards)
    System.out.println(s)     //   Print the input-String
  );                          //  End of loop
}                             // End of method
Kevin Cruijssen
fuente
2
No puedo verificar en el momento, pero ¿por qué no poner la ifcondición dentro del forbloque de condiciones?
Cliffroot
@cliffroot Se es en el forbucle.
Okx
1
@Okx quise decir condición cuando el forciclo debería terminar para que no sea explícito return. La segunda expresión dentro de la declaración.
cliffroot
@cliffroot Ah, lo entiendo.
Okx
1
Lo haría int n=2y 1f/n++trabajaría?
JollyJoker
9

Mathematica, 43 bytes

(n=1;While[RandomInteger@n>0,Print@#;n++])&

JungHwan Min guardó 1 byte (arriba) y sugirió algo mejor (abajo)

Mathematica, 37 bytes

For[n=1,RandomInteger@n++>0,Print@#]&
J42161217
fuente
1
RandomInteger@n!=0es lo mismo que RandomInteger@n<1en este caso, y n++se puede combinar con RandomInteger@n. Además, Fores (casi siempre) más corto que While: -5 bytesFor[n=1,RandomInteger@n++>0,Print@#]&
JungHwan Min
"Por" gana! También
publiqué
For[n=1,!n∣Hash[# n++],Print@#]&también funcionaría a 34 bytes, suponiendo que el hash sea bastante aleatorio. Sin embargo, la aleatoriedad depende de la entrada. Por ejemplo, intente% /@ Alphabet[]
Kelly Lowder
8

Clojure, 61 56 bytes

Oh, ¿por qué no fui con un foren primer lugar? Pero en realidad para ser pedante doseqhay que usarlo como forse evalúa perezosamente.

#(doseq[n(range):while(>(rand-int(+ n 2))0)](println %))

Original:

#(loop[n 2](if(>(rand-int n)0)(do(println %)(recur(inc n)))))
NikoNyrh
fuente
no (>(+(rand-int n)2)0)siempre es cierto?
Cliffroot
Ah, buena captura, ¡pretendía aumentar n!
NikoNyrh
8

> <> , 124 112 bytes

i:0( ?v
 &5a ~/
&p0[^ >"\_\^x0!>"0&1+:&p1&:&p2&:&p3&:&p4&:&p0&1+:&p3&:&p4&:
=?v[/!}l]:?!;1
{:   ?^  >
:o>_ {:?!^

Pruébalo en línea! (También puede verlo en el área de juegos para peces , pero debido a algunos errores, debe agregar un }después len la cuarta línea y agregar un montón de nuevas líneas después del código para que funcione correctamente).

La aleatoriedad es complicada en> <>. La única instrucción aleatoria es x, que selecciona la dirección del pez al azar entre cuatro opciones (izquierda, derecha, arriba y abajo), por lo que convertir eso en algo con probabilidad 1 / n no es sencillo.

La forma en que lo hace este código es usando las capacidades de auto-modificación de> <> para construir una Torre de Aleatoriedad debajo del código, por lo que en la cuarta etapa, por ejemplo, el código se ve así:

i:0( ?v
 &5a ~/
&p0[^ >"\_\^x0!>"0&1+:&p1&:&p2&:&p3&:&p4&:&p0&1+:&p3&:&p4&:
=?v[/!}l]:?!;1
{:   ?^  >
:o>_ {:?!^
>!0x^
\  _\
>!0x^
\  _\
>!0x^
\  _\
>!0x^
\  _\

El pez comienza en la parte inferior de la torre. En cada nivel de la torre, xestá atrapado entre dos espejos, por lo que los peces solo pueden escapar yendo hacia la izquierda o hacia la derecha. Cualquiera de estas direcciones envía al pez al siguiente nivel de la torre, pero ir a la izquierda también empuja 0a la pila. Para cuando el pez llega a la cima de la torre, la pila contiene algún número de 0s, y este número sigue una distribución binomial con n intentos y p  = 1/2.

Si la longitud de la pila es 0 (que tiene una probabilidad de 1/2 n ), el programa se detiene. Si la longitud es 1 (con probabilidad n / 2 n ), el pez imprime la entrada y una nueva línea y construye otro nivel de la torre. Si la longitud es otra cosa, el pez descarta la pila y vuelve al fondo de la torre. De hecho, de las posibilidades que realmente hacen algo, n de ellas imprimen la cadena de entrada y una de ellas detiene el programa, dando las probabilidades requeridas.

No un arbol
fuente
7

Python 3 , 72 69 66 bytes

  • Guardado 3 bytes gracias a Jonathan Allan : Importar taquigrafía y comenzar a contar desde 2.
  • Ahorró 3 bytes gracias a L3viathan : Randint puntiagudo () fue inclusivo y también se acortó mientras que la condición.
from random import*
s=input();i=1
while randint(0,i):print(s);i+=1

Pruébalo en línea!

officialaimm
fuente
1
Hay una configuración para desactivar el caché de salida, como así
Jonathan Allan
2
Yo creo que es aceptable para ser "off" para n grande (no puedo conseguir cerebral Inglés en marcha, "... (y eso que el respeto de las características, sin tener en cuenta la pseudo-aleatoriedad y grandes problemas de números) ..." sin tener en cuenta - ¿verdad?) Si es así, entonces puedes hacerlo random()<1/i.
Jonathan Allan
1
¿No comienza esto con probabilidad ⅓? randintes inclusivo Entonces podría acortar esa línea awhile randint(0,i):print(s);i+=1
L3viathan
1
Se me ocurrió la misma solución.
Esolanging Fruit
Enlace TIO actualizado. Ahora el recuento de bytes es igual que la versión de punto flotante también.
Jonathan Allan
6

QBIC , 19 17 bytes

Caída =1, cambio de condicionales, guardado 2 bytes

{p=p+1~_rp||?;\_X

Explicación

{       Infinitely DO
p=p+1   Add 1 to p (p starts as 0, so on first loop is set to 1, then 2 etc...)
~       IF
  _rp|| a random number between 0 and p
        (implicitly: is anything but 0)
?;      THEN print A$ (which gets read from the cmd line)
\_X     ELSE QUIT
        END IF and LOOP are auto-added at EOF
Steenbergh
fuente
6

Braingolf , 23 bytes

#|V12[R!&@v!r?<1+>1+]|;

Pruébalo en línea!

Genera un número aleatorio xdonde 0 <= x < n+1, termina si xes 0, de lo contrario aumenta ny repite. El separador es|

Explicación:

#|V12[R!&@v!r?<1+>1+]|;  Implicit input of commandline args to stack
#|                       Push |
  V                      Create stack2 and switch to it
   12                    Push 1, then 2
     [..............]    Do-While loop, will run indefinitely unless conditional skips
                         Closing bracket
      R                  Return to stack1
       !&@               Print entire stack without popping
          v              Switch to stack2
           !r            Generate random number 0 <= x < n where n is last item on stack
             ?           If last item is greater than 0..
              <          ..Move first item to end of stack
               1+        ..and increment, this is the loop counter number
                 >       ..Move back
                  1+     ..and increment, this is the upper range of the RNG
                    ]    ..end loop
                     |   Endif
                      ;  Suppress implicit output
Skidsdev
fuente
6

Alice , 18 bytes

/?!\v
\iO/>]qhUn$@

Pruébalo en línea!

Explicación

/     Reflect to SE. Switch to Ordinal.
i     Read all input as a string and push it to the stack.
!     Store the string on the tape.
/     Reflect to E. Switch to Cardinal.
>     Ensure that the IP moves east. This begins the main loop.

  ]   Move the tape head to the right. We'll be using the tape head's 
      position as a counter variable. Note that this tape head is independent
      of the one used in Ordinal mode to point at the input string.
  q   Push the tape head's position to the stack.
  h   Increment it (so that it's 2 initially).
  U   Get a uniformly random number in [0,n).
  n   Logical NOT. Gives 1 with probability 1/n and 0 otherwise.
  $@  Terminate the program if we got a  1.
  \   Reflect to NE. Switch to Ordinal.
  ?   Retrieve the input from the tape.
  O   Print it with a trailing linefeed.
  \   Reflect to E. Switch to Cardinal.

v     Send the IP south where it runs into the > to start the next
      loop iteration.
Martin Ender
fuente
3

Carbón , 14 bytes

A²γW‽γ«θ_A⁺γ¹γ

Pruébalo en línea! El enlace es a la versión detallada del código. Usos _como separador. Nota: el almacenamiento en caché de salida está desactivado, ¡así que no martillee el servidor de Dennis!

Neil
fuente
3

MATL , 9 bytes

`G@QYrq]x

Pruébalo en línea!

Explicación

`        % Do...while
  G      %   Push input
  @      %   Push iteration index k, starting at 1
  QYrq   %   Random integer uniformly distributed in {0, 1, ..., k}. This is the
         %   loop condition. If non-zero (which occurs with probability k/(1+k))
         %   proceed with next iteration; else exit loop
]        % End
x        % Delete, as there are one too many strings. Implicitly display the stack
Luis Mendo
fuente
3

Perl 6 ,  50 41 38 36  26 bytes

{put $_//last for (($,$_),*⊎$_...*).map(*.pick)}

Intentalo

{eager ->{(++$).rand>.5??.put!!last}...*}

Intentalo

{eager ->{(++$).rand>.5??.put!!0}...0}

Intentalo

{eager ->{(++$).rand>.5&&.put}...!*}

Intentalo

.put while (++$/).rand>.5

(con -nargumento de línea de comandos)

Intentalo

Brad Gilbert b2gills
fuente
3

Python 3 , 55 bytes

v=s=input();i=2
while hash(v)%i:print(s);i+=1;v=hash(v)

Explicación

Para ahorrar tener que importar al azar, he explotado el hecho de que el hash incorporado se siembra aleatoriamente cada vez que se activa un proceso de Python (al menos en MacOS). Cada hash del último hash debería generar una serie de enteros pseudoaleatorios.

Si el hash es lo suficientemente seudoaleatorio, el módulo con ies cero con probabilidad 1/i.

Notas

Me molesta un poco el hash redundante, pero sin una asignación temporal o en condiciones en Python, estoy un poco atascado.

Kit Ham
fuente
¿Sabes si el hashing iterado siempre cubrirá el espacio completo de números aleatorios, o puede quedar atascado en un ciclo? La mayoría de los lenguajes de programación usan algoritmos hash aleatorios hoy en día para evitar que las personas causen intencionalmente colisiones hash, pero no estoy seguro de cómo las garantías de aleatoriedad de los algoritmos hash se comparan con las de un PRNG.
Es un punto justo. Y no estoy seguro, tomaría algún análisis de la implementación de hash de Python para verificar (sin una verificación más exhaustiva). Pensé que era una solución divertida, incluso si existe la posibilidad de que no sea 100% pseudoaleatorio = p
Kit Ham
I'm a little bothered...recursividad?
Felipe Nardi Batista
3

DO#

Esta es la misma longitud que la respuesta superior de C #, pero:

using System;s=>{var x=(1<<31)/new Random().Next();for(;++x>0;)Console.Write(s+" ");}

Solo quería señalar que algunas matemáticas pueden producir la probabilidad correcta.

int.MaxValue/new Random().Next()-1

Es equivalente a

(int)(1 / new Random().NextDouble()) - 1;

Y la función f (x) = 1 / x-1 es:

f (1) = 0

f (1/2) = 1

f (1/3) = 2

f (1/4) = 3

Entonces, 1/2 oportunidad de ser redondeado a 0, 1/6 una oportunidad de ser redondeado a 1 y 1 / (n + 1) (n + 2) una probabilidad de ser redondeado a n.

Quizás algún otro idioma podría capitalizar esto.

EDITAR: solucionó mi error

Pensé en algo para hacerlo más pequeño.

EDITAR EDITAR: Estoy equivocado. Extrajo al azar del ciclo porque si se evalúa varias veces, no funcionará.

EDITAR EDITAR EDITAR: me deshice de la variable i. Voy a dejar de intentar reducirlo ahora. No, mintió. Se deshizo de otro byte.

Geoffrey
fuente
2

C, 41 bytes

n;f(char*s){for(n=1;rand()%++n;puts(s));}

Asume que randestá sembrado. Pruébalo en línea!

MD XF
fuente
"Asume que randestá sembrado". - ¿Es una suposición válida? randel estándar requiere que tenga un valor semilla fijo de 1 por defecto y todas las implementaciones que conozco hacen exactamente eso. Si esta función solo hace lo que el desafío pide cuando se combina con otro código, creo que se debe incluir otro código en la respuesta y en el recuento de bytes.
hvd
2

braingasm , 22 bytes

editar: Mismo recuento de bytes, pero me di cuenta de que podía colarse en la nueva Lfunción de imitación de cinta .

,[>,]>L+[+$rzQ>[.>]:>]

Usos 0como separador. Funciona así:

,[>,]                   Read a byte and move to next cell until end of input.
     >                  After the loop we're in an empty cell;
                          Leave it empty and move to the next.
      L                 Set tape limit here:
                          The tape will then wrap around if we move further.
       +                Increase current cell by one.
                          This cell will be our counter.
        [            ]  Loop until the counter is zero.
                          That won't happen, so it's an infinite loop.
         +              Increase again, so the first time the counter is 2.
          $r            Get a random number, 0 <= r > current cell
            zQ          Quit the program if that random number was 0
              >         Wrap around to the start of the tape.
               [.>]     Print the input stored on the tape
                          The loop will stop at the blank cell.
                   :    Print the blank cell as a number ("0")
                    >   Go to the next (last) cell
daniero
fuente
2

Python , 54 bytes

lambda s:int(1/random()-1)*(s+'|')
from random import*

Pruébalo en línea!

Genera el número de copias como floor(1/p)-1con puniformemente elegido del intervalo de la unidad. El número de copias es ncuando 1/p-1cae entre ny n+1, que sucede cuando 1/(n+2) < p < 1/(n+1). Esto sucede con probabilidad 1/(n+1)-1/(n+2)o 1/((n+1)*(n+2). Esta es la probabilidad deseada de generar ncopias: 1/2prob de 0, 1/6prob de 1, 1/12prob de 2, ...

xnor
fuente
¿Por qué está form random import*en la parte inferior?
CalculatorFeline
@CalculatorFeline El orden no importa. La definición de la función funciona en ambos sentidos.
xnor
@CalculatorFeline Para soltar bytes al no escribir f=y colocarlo en el encabezado TIO
Sr. Xcoder
Eso tiene sentido.
CalculatorFeline
2

C ++, 97 96 57 bytes

Aquí mi primer intento en codegolf :)

#include<iostream>
int main(){std::string S;std::cin>>S;int i=1;while(rand()%++i)puts(S.data());}

Ahorré un byte usando for

#include<iostream>
int main(){std::string S;std::cin>>S;for(int i=1;rand()%++i;)puts(S.data());}

Se guardaron 39 bytes ya que nadie parece contar los incluye

void p(string S){for(int i=1;rand()%++i;)puts(S.data());}

sin golf

#include <iostream>
int main()
{
  // Create and read string from inputstream
  std::string S;
  std::cin >> S;       

  // rand % i: create random int in range [0, i-1]
  // Zero is seen as false and all positive int as true
  int i = 1;
  while (rand() % ++i) 
    puts(S.data());    
}
Michiel uit het Broek
fuente
Puede tomar la cadena como argumento desde la línea de comando
Maliafo
Las cuentas se cuentan, a menos que encuentre un compilador que las incluya de manera predeterminada
Felipe Nardi Batista el
2

F #, 161 bytes

Definitivamente no es el mejor lenguaje para el golf, pero decidí probarlo (además, no sé nada sobre F #, por lo que cualquier consejo sobre cómo mejorar mi respuesta será bienvenido).

let f s=
 let r,z=System.Random(),(<>)0
 let p _=printfn"%s"s
 seq {for i in 2|>Seq.unfold(fun i->Some(i,i+1))do yield r.Next(i)}|>Seq.takeWhile z|>Seq.iter p

Ejecutar con:

[<EntryPoint>]
let main argv =
    "test" |> f
    0

Escribe una nueva línea como separador.

Charlie
fuente
2

Rubí , 29 + 1 = 30 bytes

Usa la -nbandera.

i=1;puts$_ while i>rand(i+=1)

Pruébalo en línea!

Tinta de valor
fuente
Como está tomando exactamente una línea de entrada, puede usar $. variable especial en lugar de i . Podría decirse también puede reemplazar puts$_con print, pero no está claro que las reglas son compatibles.
histocrat
2

JS (ES6), 47 bytes

x=>{for(i=1;Math.random()<i/(i+1);i++)alert(x)}

A diferencia de la otra respuesta de ES6, esta utiliza un bucle for y bombas de alerta en lugar de recurrencia. El separador que se imprime cuando se detiene el programa no está definido.


fuente
2

PowerShell, 31 bytes

for($i=2;random($i++)){"$args"}

Get-Random $igenera un nwhere 0 <= n < $i, el separador es una nueva línea implícita.

TessellatingHeckler
fuente
1
29 bytes?
Veskah
1

Python, 75 bytes

La otra respuesta de Python es más corta, pero quería probarla de otra manera:

from random import*
f=lambda d=1,s=input():randint(0,d)and s+'!'+f(d+1)or''
L3viatán
fuente