Calcular el dígito de verificación ISBN-13

28

Escriba una función que, dados los primeros 12 dígitos de un código ISBN-13 , calculará todo el ISBN calculando y agregando un dígito de verificación apropiado.

La entrada de su función es una cadena que contiene los primeros 12 dígitos del ISBN. Su salida es una cadena que contiene los 13 dígitos.

Especificación formal

Escriba una función que, cuando se le da una cadena s que consiste completamente de exactamente 12 dígitos decimales (y no otros caracteres), devuelve una cadena t con las siguientes propiedades:

  • t consta de exactamente 13 dígitos decimales (y ningún otro carácter);
  • s es un prefijo de t ;
  • la suma de todos los dígitos en posiciones impares en t (es decir, el primero, tercero, quinto, etc.), más tres veces la suma de todos los dígitos en posiciones pares en t (es decir, el segundo, cuarto, sexto, etc.), es un múltiplo de 10.

Ejemplo / caso de prueba

Entrada
978030640615

Salida
9780306406157

Condición de victoria

Como un desafío de , gana la respuesta más corta.

Kevin Brown
fuente
1
¿Se supone que la entrada y la salida contienen guiones o solo los dígitos?
sepp2k
1
Se actualizó la descripción, la entrada y la salida son solo los dígitos
Kevin Brown
¿La salida del ISBN-13 completo también es aceptable?
Sr. Llama
66
Tenga en cuenta que las preguntas realmente deberían ser independientes, por lo que sería útil incluir una descripción del algoritmo aquí.
FlipTack
Para mí la publicación anterior es pobre de prueba de ejemplo ... Habría al menos 10 isbn y uno de ellos debe devolver 0 como último dígito ...
RosLuP

Respuestas:

14

Golfscript - 25 caracteres

{...+(;2%+{+}*3-~10%`+}:f

La versión completa del programa solo tiene 19 caracteres

...+(;2%+{+}*3-~10%

Vuelve aquí para analizarlo más tarde. Mientras tanto, mira mi vieja respuesta no inspirada

Golfscript - 32 caracteres

Similar al cálculo del número de luhn

{.{2+}%.(;2%{.+}%+{+}*~)10%`+}:f

Análisis para 978030640615

{...}:f this is how you define the function in golfscript
.       store an extra copy of the input string
        '978030640615' '978030640615'
{2+}%   add 2 to each ascii digit, so '0'=>50, I can get away with this instead
        of {15&}% because we are doing mod 10 math on it later
        '978030640615' [59 57 58 50 53 50 56 54 50 56 51 55]
.       duplicate that list
        '978030640615' [59 57 58 50 53 50 56 54 50 56 51 55] [59 57 58 50 53 50 56 54 50 56 51 55]
(;      trim the first element off
        '978030640615' [59 57 58 50 53 50 56 54 50 56 51 55] [57 58 50 53 50 56 54 50 56 51 55]
2%      select every second element
        '978030640615' [59 57 58 50 53 50 56 54 50 56 51 55] [57 50 50 54 56 55]
{.+}%   double each element by adding to itself
        '978030640615' [59 57 58 50 53 50 56 54 50 56 51 55] [114 100 100 108 112 110]
+       join the two lists together
        '978030640615' [59 57 58 50 53 50 56 54 50 56 51 55 114 100 100 108 112 110]
{+}*    add up the items in the list
        '978030640615' 1293
~       bitwise not
        '978030640615' -1294
)       add one
        '978030640615' -1293            
10%     mod 10
        '978030640615' 7
`       convert to str
        '978030640615' '7'
+       join the strings
        '9780306406157'
gnibbler
fuente
Después de ejecutar el código a través de un intérprete, creo que puede guardar algunos caracteres (en su solución basada en Luhn de 32 caracteres) eliminando el primero {y los últimos tres caracteres; }:f. Me pregunto si se puede hacer lo mismo para la primera solución ...
Rob
@ MikeDtrick, esos caracteres son cómo GS define una función. La versión de 19 caracteres hace lo que usted sugiere, pero la pregunta pedía una "función"
gnibbler
Oh bien, gracias por la explicación.
Rob
No necesita el :f(sí, sé que las funciones se llamaban comúnmente en ese entonces).
Erik the Outgolfer
8

Python - 44 caracteres

f=lambda s:s+`-sum(map(int,s+s[1::2]*2))%10`

Python - 53 caracteres

def f(s):d=map(int,s);return s+`-sum(d+d[1::2]*2)%10`
gnibbler
fuente
Creo que f ('9780306406159') genera '97803064061598' en lugar de '9780306406157'
Eelvex
@Eelvex, la cadena de entrada siempre debe tener 12 dígitos
gnibbler
Ah, de alguna manera '9' se coló. Lo siento ...
Eelvex
7

Haskell - 54 caracteres

i s=s++show(sum[-read[c]*m|c<-s|m<-cycle[1,3]]`mod`10)

Esto requiere soporte para la comprensión de listas paralelas , que es compatible con GHC (con la -XParallelListCompbandera) y Hugs (con la -98bandera).

Joey Adams
fuente
¿No necesita incluir esa bandera en el recuento? Además de eso, se puede sustituir [1,3]por [9,7]y retire el -cual le ahorra un byte :)
ბიმო
7

APL (27 caracteres)

F←{⍵,⍕10|10-(12⍴1 3)+.×⍎¨⍵}

Estoy usando Dyalog APL como mi intérprete. Aquí hay una explicación rápida, principalmente de derecha a izquierda (dentro de la definición de la función F←{ ... }):

  • ⍎¨⍵: Ejecutar / evaluar ( ) cada ( ¨) carácter dado en el argumento correcto ( ).
  • (12⍴1 3): Reforma ( ) el vector 1 3en un 12vector de elementos (repitiendo para llenar los espacios).
  • +.×: Tome el producto punto ( +.×) de su argumento izquierdo ( (12⍴1 3)) y su argumento derecho ( ⍎¨⍵).
  • 10-: Restar de 10.
  • 10|: Encuentra el resto después de la división por 10.
  • : Formatee el número (es decir, dé una representación de caracteres).
  • ⍵,: Añadir ( ,) nuestro dígito calculado al argumento correcto.
Dillon Cower
fuente
6

PHP - 86 85 82 caracteres

function c($i){for($a=$s=0;$a<12;)$s+=$i[$a]*($a++%2?3:1);return$i.(10-$s%10)%10;}

Reformatear y explicar:

function c($i){                     // function c, $i is the input

    for($a=$s=0;$a<12;)             // for loop x12 - both $a and $s equal 0
                                    // notice there is no incrementation and
                                    // no curly braces as there is just one
                                    // command to loop through

        $s+=$i[$a]*($a++%2?3:1);    // $s (sum) is being incremented by
                                    // $ath character of $i (auto-casted to
                                    // int) multiplied by 3 or 1, depending
                                    // wheter $a is even or not (%2 results
                                    // either 1 or 0, but 0 == FALSE)
                                    // $a is incremented here, using the
                                    // post-incrementation - which means that
                                    // it is incremented, but AFTER the value
                                    // is returned

    return$i.(10-$s%10)%10;         // returns $i with the check digit
                                    // attached - first it is %'d by 10,
                                    // then the result is subtracted from
                                    // 10 and finally %'d by 10 again (which
                                    // effectively just replaces 10 with 0)
                                    // % has higher priority than -, so there
                                    // are no parentheses around $s%10
}
Aurel Bílý
fuente
Exactamente el enfoque que tomé en mi respuesta de C #. Parece que PHP es ~ 9 caracteres más eficiente!
Nellius
6

Windows PowerShell, 57

filter i{$_+(990-($_-replace'(.)(.)','+$1+3*$2'|iex))%10}
Joey
fuente
5

Haskell, 78 71 66 caracteres

i s=s++(show$mod(2-sum(zipWith(*)(cycle[1,3])(map fromEnum s)))10)
sepp2k
fuente
5

Ruby - 73 65 caracteres

f=->s{s+((2-(s+s.gsub(/.(.)/,'\1')*2).bytes.inject(:+))%10).to_s}
gnibbler
fuente
"\\1"-> '\1'?
Nemo157 01 de
@Nemo, gracias, mi rubí está un poco oxidado
gnibbler
Use la sintaxis Ruby 1.9 f=->s{...}. Ahorra 6 caracteres. También escriba en s<<(...).to_slugar de agregar 48 y use Fixnum#chr.
Hauleth
4

C # (94 caracteres)

string I(string i){int s=0,j=0;for(;j<12;)s+=(i[j]-48)*(j++%2<1?1:3);return i+((10-s%10)%10);}

Con saltos de línea / espacios en blanco para facilitar la lectura:

string I(string i) 
{ 
    int s = 0, j = 0;
    for (; j < 12; )
        s += (i[j] - 48) * (j++ % 2 < 1 ? 1 : 3); 
    return i + ((10 - s % 10) % 10); 
}

Probado en varios ISBN de libros en mi estante, ¡así que sé que está funcionando!

Nellius
fuente
4

Python - 91 , 89

0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
|         |         |         |         |         |         |         |         |         |
 def c(i):return i+`(10-(sum(int(x)*3for x in i[1::2])+sum(int(x)for x in i[::2]))%10)%10`
grokus
fuente
Los espacios son opcionales entre el primer argumento y for(y inel tercero) en una comprensión de la lista siempre que el analizador pueda dividirlo (sin usar un nombre de variable). -2 caracteres allí.
Nick T
4

Perl, 53 caracteres

sub i{$_=shift;s/(.)(.)/$s+=$1+$2*3/ge;$_.(10-$s)%10}
ninjalj
fuente
4

C # - 89 77 caracteres

string I(string s){return s+(9992-s.Sum(x=>x-0)-2*s.Where((x,i)=>i%2>0).Sum(x=>x-0))%10;}

Formateado para facilitar la lectura:

string I(string s)
{
    return s +
            (9992
            - s.Sum(x => x - 0)
            - 2 * s.Where((x, i) => i%2 > 0).Sum(x => x - 0)
            ) % 10;
}

No multiplicamos por uno o tres, simplemente agregamos todo, además agregamos todos los caracteres pares una vez más, multiplicados por dos.

9992 es lo suficientemente grande como para que la suma de todos los caracteres ASCII sea menor que eso (para que podamos modificar por 10 y asegurarnos de que el resultado sea positivo, no es necesario modificar por 10 dos veces), y no es divisible por cero porque sumamos arriba todos esos 2 * 12 * 48 adicionales (doce dígitos ASCII, pesados ​​por 1 y 3) == 1152, lo que nos permite ahorrar un carácter adicional (en lugar de restar dos veces 48, restamos 0 solo para convertir de char a int, pero en lugar de 990, necesitamos escribir 9992).

Pero, de nuevo, aunque mucho menos hermoso ;-), esta solución de la vieja escuela nos lleva a 80 caracteres (pero esto es casi compatible con C):

string T(string i){int s=2,j=0;for(;j<12;)s+=i[j]*(9-j++%2*2);return i+s%10;}
Mormegil
fuente
4

J - 55 45 38

f=:3 :'y,":10|10-10|+/(12$1 3)*"."0 y'

p.ej

f '978030640615'
9780306406157

vieja forma:

f=:,":@(10(10&|@-)(10&|@+/@((12$1 3)*(i.12)&(".@{))))
Eelvex
fuente
1
(i.12)(".@{)ypuede ser reemplazado con"."0 y
J Guy
3

Ruby - 80 caracteres

def f s;s+(10-s.bytes.zip([1,3]*6).map{|j,k|(j-48)*k}.inject(:+)%10).to_s[0];end
Nemo157
fuente
3

dc, 44 caracteres

[d0r[I~3*rI~rsn++lndZ0<x]dsxx+I%Ir-I%rI*+]sI

Invocar como lIx, por ejemplo:

dc -e'[d0r[I~3*rI~rsn++lndZ0<x]dsxx+I%Ir-I%rI*+]sI' -e '978030640615lIxp'
ninjalj
fuente
3

Q, 36 caracteres

{x,-3!10-mod[;10]sum(12#1 3)*"I"$'x}
tmartin
fuente
2

D - 97 caracteres

auto f(string s){int n;foreach(i,c;s)n+=((i&1)*2+1)*(c-48);return s~cast(char)((10-n%10)%10+48);}

Formateado de manera más legible:

auto f(string s)
{
    int n;

    foreach(i, c; s)
        n += ((i & 1) * 2 + 1) * (c - 48);

    return s ~ cast(char)((10 - n % 10) % 10 + 48);
}

Sin embargo, la verbosidad del operador de reparto de D definitivamente hace que sea más difícil escribir código obsesivamente corto.

Jonathan M Davis
fuente
2

Java - 161 caracteres :(

int b[]=new int[a.length];
int d=0,n=0,j=1;
for(char c:a.toCharArray())b[d++]=Integer.valueOf(c+"");
for(int i:b)n+=(j++%2==0)?(i*3):(i*1);
return a+(10-(n%10));
Octavian A. Damiean
fuente
Esta respuesta no satisface el primer requisito, ya que no es una función.
Han
1

Q (44 caracteres)

f:{x,string 10-mod[;10]0+/sum@'2 cut"I"$/:x}
skeevey
fuente
Esto es realmente incorrecto, creo
skeevey
1

Scala 84

def b(i:String)=i+(10-((i.sliding(2,2).map(_.toInt).map(k=>k/10+k%10*3).sum)%10)%10)

Pruebas:

val isbn="978030640615"
b(isbn)

Resultado:

"9780306406157"
usuario desconocido
fuente
1

C, 80 79 caracteres

La función modifica la cadena en su lugar, pero devuelve el puntero de cadena original para satisfacer los requisitos del problema.

s;char*f(char*p){for(s=2;*p;s+=7**p++)s+=9**p++;*p++=48+s%10;*p=0;return p-13;}

Alguna explicación: en lugar de restar 48 (el valor ASCII del dígito 0) de cada carácter de entrada, el acumulador sse inicializa de modo que sea un módulo 10 igual a 48 + 3 * 48 + 48 + 3 * 48 ... + 48 + 3 * 48 = 24 * 48 = 1152. El paso 10-sumse puede evitar acumulando spor sustracción en lugar de sumar. Sin embargo, el operador del módulo %en C no daría un resultado útil si sfuera negativo, por lo que en lugar de usar s-=los multiplicadores 3 y 1 se reemplazan por -3 = 7 módulo 10 y -1 = 9 módulo 10, respectivamente.

Arnés de prueba:

#include <stdio.h>
#define N 12
int main()
{
     char b[N+2];
     fgets(b, N+1, stdin);
     puts(f(b));
     return 0;
}
han
fuente
1

Groovy 75 , 66 caracteres

i={int i;it+(10-it.inject(0){t,c->t+(i++&1?:3)*(c as int)}%10)%10}

utilizar:

String z = "978030640615"
println i(z)

-> 9780306406157
Armand
fuente
1

APL (25)

{⍵,⍕10-10|+/(⍎¨⍵)×12⍴1,3}
marinus
fuente
El algo tiene que terminar con 10 | porque de lo contrario podría devolver 10 en lugar de 0
RosLuP
1

Perl 6 , 29 bytes

{$_~-:1[.comb «*»(1,3)]%10}

Pruébalo en línea!

nwellnhof
fuente
Entonces, ¿la base 1 se puede usar como un sustituto de la suma? ¡Interesante!
Jo King el
2
@JoKing En realidad es un truco muy antiguo en el mundo de APL y J :)
Bubbler el
Resultado incorrecto para 978186197371 parece ser 8 no 9 ...
RosLuP
Disculpe Creo que había pegado el número equivocado
RosLuP
1

Python 2 , 78 76 bytes

lambda n:n+`10-(sum(int(a)+3*int(b)for a,b in zip(n[::2],n[1::2]))%10or 10)`

Pruébalo en línea!

Toma una cuerda como argumento.

Explicación:

Usando la notación de corte de python, convierte una cadena en una lista de pares de caracteres. ("978030640615" -> [("9", "7"), ("8", "0"), ("3", "0"), ("6", "4"), ("0 "," 6 "), (" 1 "," 5 ")])

Para esa lista de pares, convierte cada elemento en un entero y devuelve un + 3b.

Suma todos los resultados.

Obtiene el módulo de suma 10, O 10 si el resto es 0. (Esto evita que el último dígito sea 10 en lugar de 0.)

Elimina el resto de 10 para obtener el dígito de verificación.

Convierte el dígito de verificación calculado en una cadena mediante una expresión de retroceso obsoleta.

Devuelve el número original más el dígito de verificación calculado.

Editar:

Ahorró 2 byes al eliminar espacios (¡gracias Jo King !).

Triggernometry
fuente
Puede eliminar los espacios antes foryor
Jo King
Como resultado, 978186197371 tiene el último dígito 8 y no 9 ... obtengo ese número del único enlace que
imprimo
Disculpe, creo que he pegado el número equivocado ...
RosLuP
1

APL (Dyalog Unicode) , SBCS de 18 bytes

Función de prefijo tácito anónimo que toma una cadena como argumento. Usando el enfoque de Bubbler .

⊢,∘⍕10|⍎¨+.×9 7⍴⍨≢

Pruébalo en línea!

 longitud del argumento (12)

9 7⍴⍨ remodelar cíclicamente [9,7]a esa longitud

+.× producto de punto de lo siguiente con eso:

⍎¨ `evaluar cada personaje

10| mod-10 de eso

,∘⍕ anteponga lo siguiente a la stringificación de eso:

 el argumento no modificado

Adán
fuente
1

dc , 25 bytes

dn[A~9z^8+*rd0<M+]dsMxA%p

Pruébalo en línea!

Sé que ya hay una respuesta de CC aquí, pero 25 <44, así que creo que siento 19 bytes de acuerdo. Esto utiliza el hecho de que 8+9^zes equivalente a cualquiera -3o -1mod 10 dependiendo de si z es par o impar. Así que solía A~dividir el número en dígitos en la pila, pero a medida que construyo la pila multiplico cada dígito por 8+9^zdonde z es el tamaño actual de la pila. Luego los agrego a todos a medida que se despliega la pila de funciones e imprimo el último dígito.

Sophia Lechner
fuente
0

MATLAB - 82 caracteres

function c(i)
[i num2str(mod(10-mod(sum(str2num(i(:)).*repmat([1;3],6,1)),10),10))]
Grifo
fuente
0

R, 147 caracteres

f=function(v){s=as.numeric(strsplit(v,"")[[1]]);t=0;for(i in 1:12)if(i%%2==0)t=t+s[i]*3 else t=t+s[i];paste(v,(10-(t%%10))%%10,collapse="",sep="")}

Uso:

f("978030640615")
[1] "9780306406157"
Paolo
fuente
0

J, 25

,[:":10|0(-+/)"."0*1 3$~#
   f =:, [: ": 10 | 0 (- + /)". "0 * 1 3 $ ~ #
   f '978030640615'
9780306406157
efímero
fuente