Calcola la cifra di controllo ISBN-13


28

Scrivi una funzione che, date le prime 12 cifre di un codice ISBN-13 , calcolerà l'intero codice ISBN calcolando e aggiungendo una cifra di controllo appropriata.

L'input della tua funzione è una stringa contenente le prime 12 cifre del codice ISBN. Il suo output è una stringa contenente tutte e 13 le cifre.

Specifiche formali

Scrivi una funzione che, quando viene data una stringa s composta interamente da esattamente 12 cifre decimali (e nessun altro carattere), restituisce una stringa t con le seguenti proprietà:

  • t consiste esattamente di 13 cifre decimali (e nessun altro carattere);
  • s è un prefisso di t ;
  • la somma di tutte le cifre in posizioni dispari in t (cioè la prima, la terza, la quinta, ecc.), più tre volte la somma di tutte le cifre in posizioni pari in t (cioè la seconda, la quarta, la sesta, ecc.), è una multiplo di 10.

Esempio / caso di test

Ingresso
978030640615

Produzione
9780306406157

Condizione di vittoria

Come una sfida di , vince la risposta più breve.


1
Si suppone che l'input e l'output contengano trattini o solo le cifre?
sepp2k,

1
Aggiornata la descrizione, l'input e l'output sono solo le cifre
Kevin Brown,

Anche l'output del codice ISBN-13 completo è accettabile?
Mr. Llama

6
Nota che le domande dovrebbero davvero essere autosufficienti, quindi sarebbe utile includere una descrizione dell'algoritmo qui.
FlipTack

Per me sopra post è scarso di esempio di test ... Ci sarebbero almeno 10 isbn e uno di questi deve restituire 0 come ultima cifra ...
RosLuP

Risposte:


14

Golfscript - 25 caratteri

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

L'intera versione del programma è di soli 19 caratteri

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

Torna qui per un'analisi successiva. Nel frattempo controlla la mia vecchia risposta non ispirata

Golfscript - 32 caratteri

Simile al calcolo del numero luhn

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

Analisi per 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'

Dopo aver eseguito il codice attraverso un interprete, penso che puoi salvare alcuni caratteri (nella tua soluzione basata su Luhn a 32 caratteri) eliminando il primo {e gli ultimi tre caratteri; }:f. Mi chiedo se si possa fare la stessa cosa per la prima soluzione ...
Rob

@MikeDtrick, quei personaggi sono il modo in cui GS definisce una funzione. La versione a 19 caratteri fa ciò che suggerisci, ma la domanda ha richiesto una "funzione"
gnibbler,

Oh ok, grazie per la spiegazione.
Rob,

Non è necessario il :f(sì, so che le funzioni erano comunemente chiamate allora).
Erik the Outgolfer,

8

Python - 44 caratteri

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

Python - 53 caratteri

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

Penso che f ('9780306406159') produca '97803064061598' invece di '9780306406157'
Eelvex

@Eelvex, la stringa di input dovrebbe essere sempre di 12 cifre
gnibbler

Ah, in qualche modo '9' si è intrufolato. Mi dispiace ...
Eelvex,

7

Haskell - 54 caratteri

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

Ciò richiede il supporto per la comprensione di elenchi paralleli , che è supportato da GHC (con la -XParallelListCompbandiera) e Hugs (con la -98bandiera).


Non è necessario includere quella bandiera nel conteggio? Oltre a questo, è possibile sostituire [1,3]da [9,7]e rimuovere il -che consente di risparmiare un byte :)
ბიმო

7

APL (27 caratteri)

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

Sto usando Dyalog APL come mio interprete. Ecco una breve spiegazione, principalmente da destra a sinistra (all'interno della definizione della funzione F←{ ... }):

  • ⍎¨⍵: Esegue / valuta ( ) ogni ¨carattere ( ) indicato nell'argomento corretto ( ).
  • (12⍴1 3): Rimodella ( ) il vettore 1 3in un 12vettore -elemento (ripetendo per riempire gli spazi).
  • +.×: Prendi il punto prodotto ( +.×) del suo argomento sinistro ( (12⍴1 3)) e il suo argomento destro ( ⍎¨⍵).
  • 10-: Sottrai da 10.
  • 10|: Trova il resto dopo la divisione per 10.
  • : Formatta il numero (ovvero, dai una rappresentazione di carattere).
  • ⍵,: Aggiungi ( ,) la nostra cifra calcolata all'argomento giusto.

6

PHP - 86 85 82 caratteri

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

Riformattazione e spiegazione:

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
}

Esattamente l'approccio che ho seguito nella mia risposta C #. Sembra che PHP sia ~ 9 caratteri più efficiente!
Nellius,

6

Windows PowerShell, 57

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

5

Haskell, 78 71 66 caratteri

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

5

Rubino - 73 65 caratteri

f=->s{s+((2-(s+s.gsub(/.(.)/,'\1')*2).bytes.inject(:+))%10).to_s}

"\\1"-> '\1'?
Nemo157,

@Nemo, grazie, il mio rubino è un po 'arrugginito
roditore

Usa la sintassi di Ruby 1.9 f=->s{...}. Salva 6 caratteri. Inoltre, scrivi s<<(...).to_sinvece di aggiungere 48 e usa Fixnum#chr.
Hauleth

4

C # (94 caratteri)

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 interruzioni di riga / spazi bianchi per la leggibilità:

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

Testato su diversi codici ISBN dai libri sul mio scaffale, quindi so che funziona!


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`

Gli spazi sono facoltativi tra il primo argomento e for(e inil terzo) in una comprensione dell'elenco purché possa essere suddiviso dal parser (non usando un nome di variabile). -2 caratteri lì.
Nick T

4

Perl, 53 caratteri

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

4

C # - 89 77 caratteri

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

Formattato per la leggibilità:

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

Non moltipliciamo per uno o tre, aggiungiamo semplicemente tutto, inoltre aggiungiamo tutti i caratteri pari ancora una volta, moltiplicati per due.

9992 è abbastanza grande in modo che la somma di tutti i caratteri ASCII sia inferiore a quella (in modo che possiamo modare per 10 ed essere sicuri che il risultato sia positivo, non c'è bisogno di mod per 10 due volte) e non è divisibile per zero perché aggiungiamo su tutti quei 2 * 12 * 48 extra (dodici cifre ASCII, pesati da 1 e 3) == 1152, che ci consente di risparmiare un carattere in più (invece di sottrarre due volte 48, sottraggiamo 0 solo per convertire da char a int, ma invece di 990, dobbiamo scrivere 9992).

Ma ancora una volta, anche se molto meno bella ;-), questa soluzione di vecchia scuola ci porta a 80 caratteri (ma questo è quasi compatibile 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;}

4

J - 55 45 38

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

per esempio

f '978030640615'
9780306406157

vecchio modo:

f=:,":@(10(10&|@-)(10&|@+/@((12$1 3)*(i.12)&(".@{))))

1
(i.12)(".@{)ypuò essere sostituito con"."0 y
J Guy il

3

Rubino - 80 caratteri

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

3

dc, 44 caratteri

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

Richiamare come lIx, ad esempio:

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

3

Q, 36 caratteri

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

2

D - 97 caratteri

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

Formattato in modo più leggibile:

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

La verbosità dell'operatore del cast di D rende sicuramente più difficile scrivere codici abbreviati in modo ossessivo.


2

Java - 161 caratteri :(

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

Questa risposta non soddisfa il primo requisito, poiché non è una funzione.
han

1

Q (44 caratteri)

f:{x,string 10-mod[;10]0+/sum@'2 cut"I"$/:x}

Questo in realtà non è corretto credo
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)

test:

val isbn="978030640615"
b(isbn)

Risultato:

"9780306406157"

1

C, 80 79 caratteri

La funzione modifica la stringa in atto, ma restituisce il puntatore di stringa originale per soddisfare i requisiti 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;}

Qualche spiegazione: invece di sottrarre 48 (il valore ASCII della cifra 0) da ciascun carattere di input, l'accumulatore sviene inizializzato in modo che sia modulo 10 pari a 48 + 3 * 48 + 48 + 3 * 48 ... + 48 + 3 * 48 = 24 * 48 = 1152. Il passaggio 10-sumpuò essere evitato accumulando sper sottrazione anziché per addizione. Tuttavia, l'operatore del modulo %in C non darebbe un risultato utilizzabile se sfosse negativo, quindi invece di utilizzare s-=i moltiplicatori 3 e 1 vengono sostituiti rispettivamente da -3 = 7 modulo 10 e -1 = 9 modulo 10.

Collaudare l'imbragatura:

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

1

Groovy 75 , 66 caratteri

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

uso:

String z = "978030640615"
println i(z)

-> 9780306406157

1

APL (25)

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

L'algo deve finire con 10 | perché altrimenti potrebbe restituire 10 al posto di 0
RosLuP

1

Perl 6 , 29 byte

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

Provalo online!


Quindi la base 1 può essere utilizzata come sostituto della somma? Interessante!
Jo King,

2
@JoKing In realtà è un trucco molto vecchio nel mondo di APL e J :)
Bubbler

Risultato errato per 978186197371 sembra essere 8 non 9 ...
RosLuP

Mi scusi, penso di aver incollato il numero sbagliato
RosLuP

1

Python 2 , 78 76 byte

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

Provalo online!

Accetta una stringa come argomento.

Spiegazione:

Usando la notazione di python slice, converte una stringa in un elenco di coppie di caratteri. ("978030640615" -> [("9", "7"), ("8", "0"), ("3", "0"), ("6", "4"), ("0 "," 6 "), (" 1 "," 5 ")])

Per quell'elenco di coppie, converte ciascun elemento in un numero intero e restituisce un + 3b.

Somma tutti i risultati.

Ottiene la somma modulo 10, O 10 se il resto è 0. (Ciò impedisce alla cifra finale di essere 10 anziché 0.)

Rimuove il resto da 10 per ottenere la cifra di controllo.

Converte la cifra di controllo calcolata in una stringa tramite l'espressione backtick obsoleta.

Restituisce il numero originale più la cifra di controllo calcolata.

Modificare:

Hai salvato 2 bye rimuovendo gli spazi (grazie Jo King !).


Puoi rimuovere gli spazi prima foreor
Jo King il

Risultato per me che 978186197371 ha l'ultima cifra 8 e non 9 ... Ottengo quel numero dall'unico link che
stampo

Mi scusi, penso di aver incollato il numero sbagliato ...
RosLuP

1

APL (Dyalog Unicode) , 18 byte SBCS

Funzione di prefisso tacito anonimo che accetta la stringa come argomento. Utilizzando l'approccio di Bubbler .

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

Provalo online!

 lunghezza della discussione (12)

9 7⍴⍨ rimodellare ciclicamente [9,7]a quella lunghezza

+.× punto prodotto di quanto segue con quello:

⍎¨ `valutare ogni personaggio

10| mod-10 di quello

,∘⍕ anteporre quanto segue alla stringa di quello:

 l'argomento non modificato


1

dc , 25 byte

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

Provalo online!

So che c'è già una risposta in cc qui, ma 25 <44 quindi immagino di sentirmi bene 19 byte. Questo utilizza il fatto che 8+9^zè equivalente a uno -3o al -1mod 10 a seconda che z sia pari o dispari. Quindi uso A~per dividere il numero in cifre nello stack, ma mentre costruisco lo stack moltiplico ogni cifra per 8+9^zdove z è la dimensione dello stack corrente. Quindi li aggiungo tutti mentre la pila di funzioni si svolge e stampa l'ultima cifra.


0

MATLAB - 82 caratteri

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

0

R, 147 caratteri

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"

0

J, 25

,[:":10|0(-+/)"."0*1 3$~#
   f =:, [: ": 10 | 0 (- + /)". "0 * 1 3 $ ~ #
   f '978030640615'
9780306406157
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.