Calcular o dígito de verificação ISBN-13

28

Escreva uma função que, dados os primeiros 12 dígitos de um código ISBN-13 , calcule todo o ISBN, calculando e acrescentando um dígito de verificação apropriado.

A entrada da sua função é uma sequência que contém os primeiros 12 dígitos do ISBN. Sua saída é uma string contendo todos os 13 dígitos.

Especificação formal

Escreva uma função que, quando recebe uma sequência s composta exatamente por exatamente 12 dígitos decimais (e nenhum outro caractere), retorna uma sequência t com as seguintes propriedades:

  • t consiste em exatamente 13 dígitos decimais (e nenhum outro caractere);
  • s é um prefixo de t ;
  • a soma de todos os dígitos em posições ímpares em t (ou seja, o primeiro, terceiro, quinto etc.), mais três vezes a soma de todos os dígitos em posições pares em t (ou seja, o segundo, quarto, sexto etc.), é um múltiplo de 10.

Exemplo / caso de teste

Entrada
978030640615

Saída
9780306406157

Condição de vitória

Como um desafio do , a resposta mais curta vence.

Kevin Brown
fonte
1
A entrada e a saída devem conter traços ou apenas os dígitos?
sepp2k
1
Atualizada a descrição, a entrada e saída são apenas os dígitos
Kevin Brown
A saída do ISBN-13 completo também é aceitável?
Mr. Llama
6
Observe que as perguntas devem ser realmente independentes, portanto, seria útil incluir uma descrição do algoritmo aqui.
FlipTack
Para mim, acima do post é um exemplo pobre de teste ... Haveria no mínimo 10 isbn e um deles deve retornar 0 como último dígito ...
RosLuP

Respostas:

14

Golfscript - 25 caracteres

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

A versão completa do programa tem apenas 19 caracteres

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

Volte aqui para análise mais tarde. Enquanto isso, confira minha antiga resposta não inspirada

Golfscript - 32 caracteres

Semelhante ao cálculo do número de luhn

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

Análise 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'
mordedor
fonte
Depois de executar o código por meio de um intérprete, acho que você pode salvar alguns caracteres (na sua solução baseada em Luhn de 32 caracteres) se livrando do primeiro {e dos três últimos caracteres; }:f. Eu me pergunto se a mesma coisa pode ser feita para a primeira solução ...
Rob
@ MikeDtrick, esses caracteres são como o GS define uma função. A versão de 19 caracteres faz o que você sugere, mas a pergunta solicitou uma "função"
gnibbler
Oh ok, obrigado pela explicação.
Rob
Você não precisa do :f(sim, eu sei que as funções eram comumente nomeadas na época).
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`
mordedor
fonte
Eu acho que f ('9780306406159') gera '97803064061598' em vez de '9780306406157'
Eelvex
@Eelvex, a cadeia de entrada deve ser sempre 12 dígitos
gnibbler
Ah, de alguma forma '9' entrou furtivamente. Desculpe ...
Eelvex 02/02
7

Haskell - 54 caracteres

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

Isso requer suporte para compreensão de lista paralela , que é suportada pelo GHC (com o -XParallelListCompsinalizador) e Hugs (com o -98sinalizador).

Joey Adams
fonte
Você não precisa incluir esse sinalizador na contagem? Além disso, você pode substituir [1,3]por [9,7]e remova o -que economiza um byte :)
ბიმო
7

APL (27 caracteres)

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

Estou usando o Dyalog APL como meu intérprete. Aqui está uma explicação rápida, principalmente da direita para a esquerda (dentro da definição da função F←{ ... }):

  • ⍎¨⍵: Execute / avalie ( ) cada ¨caractere ( ) fornecido no argumento correto ( ).
  • (12⍴1 3): Remodele ( ) o vetor 1 3em um 12vetor de elemento (repetindo para preencher as lacunas).
  • +.×: Pegue o produto escalar ( +.×) de seu argumento esquerdo ( (12⍴1 3)) e seu argumento direito ( ⍎¨⍵).
  • 10-: Subtraia de 10.
  • 10|: Encontre o restante após a divisão por 10.
  • : Formate o número (ou seja, forneça uma representação de caractere).
  • ⍵,: Anexe ( ,) nosso dígito calculado ao argumento correto.
Dillon Cower
fonte
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;}

Re-formatar e explicação:

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ý
fonte
Exatamente a abordagem adotada na minha resposta c #. Parece que o PHP é ~ 9 caracteres mais eficiente!
Nellius 02/02/11
6

Windows PowerShell, 57

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

Haskell, 78 71 66 caracteres

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

Ruby - 73 65 caracteres

f=->s{s+((2-(s+s.gsub(/.(.)/,'\1')*2).bytes.inject(:+))%10).to_s}
mordedor
fonte
"\\1"-> '\1'?
Nemo157
@Nemo, obrigado, meu ruby é um pouco enferrujado
gnibbler
Use a sintaxe do Ruby 1.9 f=->s{...}. Economize 6 caracteres. Escreva também em s<<(...).to_svez de adicionar 48 e use Fixnum#chr.
Hauleth 10/03/12
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);}

Com linhas / espaços em branco para facilitar a leitura:

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); 
}

Testado em vários ISBNs de livros na minha estante, então eu sei que está funcionando!

Nellius
fonte
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
fonte
Os espaços são opcionais entre o primeiro argumento e for(e ino terceiro) em uma compreensão de lista, desde que possam ser divididos pelo analisador (sem usar um nome de variável). -2 caracteres lá.
Nick T
4

Perl, 53 caracteres

sub i{$_=shift;s/(.)(.)/$s+=$1+$2*3/ge;$_.(10-$s)%10}
ninjalj
fonte
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;}

Formatado para facilitar a leitura:

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;
}

Nós não multiplicamos por um ou três, apenas adicionamos tudo, além de adicionarmos todos os caracteres pares mais uma vez, multiplicados por dois.

9992 é grande o suficiente para que a soma de todos os caracteres ASCII seja menor que isso (para que possamos modificar por 10 e ter certeza de que o resultado é positivo, sem necessidade de modificar por 10 duas vezes) e não seja divisível por zero, porque adicionamos até todos os 2 * 12 * 48 extras (doze dígitos ASCII, ponderados por 1 e 3) == 1152, o que nos permite poupar um caractere extra (em vez de subtrair duas vezes 48, subtraímos 0 apenas para converter de char para int, mas em vez de 990, precisamos escrever 9992).

Mas, novamente, apesar de muito menos bonita ;-), essa solução antiga nos leva a 80 caracteres (mas isso é quase compatível com 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
fonte
4

J - 55 45 38

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

por exemplo

f '978030640615'
9780306406157

à moda antiga:

f=:,":@(10(10&|@-)(10&|@+/@((12$1 3)*(i.12)&(".@{))))
Eelvex
fonte
1
(i.12)(".@{)ypode ser substituído por"."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
fonte
3

dc, 44 caracteres

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

Invoque como lIx, por exemplo:

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

Q, 36 caracteres

{x,-3!10-mod[;10]sum(12#1 3)*"I"$'x}
tmartin
fonte
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);}

Formatado de forma mais legível:

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);
}

A verbosidade do operador de elenco de D definitivamente torna mais difícil escrever código obsessivamente curto.

Jonathan M Davis
fonte
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));
Otaviano A. Damiean
fonte
Essa resposta não atende ao primeiro requisito, pois não é uma função.
han
1

Q (44 caracteres)

f:{x,string 10-mod[;10]0+/sum@'2 cut"I"$/:x}
skeevey
fonte
Este é realmente incorreta eu acho
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)

Teste:

val isbn="978030640615"
b(isbn)

Resultado:

"9780306406157"
Usuário desconhecido
fonte
1

C, 80 79 caracteres

A função modifica a sequência no lugar, mas retorna o ponteiro da sequência original para atender aos requisitos do 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;}

Alguma explicação: em vez de subtrair 48 (o valor ASCII do dígito 0) de cada caractere de entrada, o acumulador sé inicializado de modo que o módulo 10 seja igual a 48 + 3 * 48 + 48 + 3 * 48 ... + 48 + 3 * 48 = 24 * 48 = 1152. A etapa 10-sumpode ser evitada acumulando spor subtração em vez de adição. No entanto, o operador do módulo %em C não forneceria um resultado utilizável ses fosse negativo; portanto, em vez de usar s-=os multiplicadores 3 e 1, são substituídos por -3 = 7 módulo 10 e -1 = 9 módulo 10, respectivamente.

Equipamento de teste:

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

Groovy 75 , 66 caracteres

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

usar:

String z = "978030640615"
println i(z)

-> 9780306406157
Armand
fonte
1

APL (25)

{⍵,⍕10-10|+/(⍎¨⍵)×12⍴1,3}
marinus
fonte
O algo tem que terminar com 10 | porque mais poderia retornar 10 no lugar de 0
RosLuP
1

Perl 6 , 29 bytes

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

Experimente online!

Nwellnhof
fonte
Então a base 1 pode ser usada como substituto da soma? Interessante!
Jo rei
2
@JoKing É realmente um velho truque no mundo da APL e J :)
Bubbler
Resultado errado para 978186197371 parece ser 8 e não 9 ...
RosLuP 9/03
Com licença, acho que colei o número errado.
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)`

Experimente online!

Toma uma string como argumento.

Explicação:

Usando a notação de fatia python, converte uma string em uma lista de pares de caracteres. ("978030640615" -> [("9", "7"), ("8", "0"), ("3", "0"), ("6", "4"), ("0 "," 6 "), (" 1 "," 5 ")])

Para essa lista de pares, converte cada item em um número inteiro e retorna + 3b.

Soma todos os resultados.

Obtém a soma do módulo 10, OU 10, se o restante for 0. (Isso impede que o dígito final seja 10 em vez de 0.)

Remove o restante de 10 para obter o dígito de verificação.

Converte o dígito de verificação calculado em uma string por meio da expressão de backtick obsoleta.

Retorna o número original mais o dígito de verificação calculado.

Editar:

Economizou 2 bytes removendo espaços (obrigado Jo King !).

Triggernometria
fonte
Você pode remover os espaços antes foreor
Jo King
Resultado: 978186197371 tem o último dígito 8 e não 9 ... Eu recebo esse número no único link que imprimo na minha solução Apl
RosLuP
Desculpe-me, acho que colei o número errado ...
RosLuP
1

APL (Dyalog Unicode) , SBCS de 18 bytes

Função de prefixo tácito anônimo, usando string como argumento. Usando a abordagem de Bubbler .

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

Experimente online!

 duração do argumento (12)

9 7⍴⍨ remodelar ciclicamente [9,7]para esse comprimento

+.× dot dot do seguinte com isso:

⍎¨ `avalie cada personagem

10| mod-10 desse

,∘⍕ Anexar o seguinte a stringing disso:

 o argumento não modificado

Adão
fonte
1

dc , 25 bytes

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

Experimente online!

Eu sei que já existe uma resposta dc aqui, mas 25 <44, então acho que me sinto bem com 19 bytes. Isso usa o fato de que 8+9^zé equivalente a um -3ou ao -1mod 10, dependendo de z ser par ou ímpar. Então, eu uso A~para dividir o número em dígitos na pilha, mas à medida que construo a pilha, multiplico cada dígito por 8+9^zonde z é o tamanho atual da pilha. Depois, adiciono todos eles à medida que a pilha de funções se desenrola e imprimo o último dígito.

Sophia Lechner
fonte
0

MATLAB - 82 caracteres

function c(i)
[i num2str(mod(10-mod(sum(str2num(i(:)).*repmat([1;3],6,1)),10),10))]
Griffin
fonte
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
fonte
0

J, 25

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