Calculer le chiffre de contrôle ISBN-13


28

Écrivez une fonction qui, compte tenu des 12 premiers chiffres d'un code ISBN-13 , calculera l'ISBN entier en calculant et en ajoutant un chiffre de contrôle approprié.

L'entrée de votre fonction est une chaîne contenant les 12 premiers chiffres de l'ISBN. Sa sortie est une chaîne contenant les 13 chiffres.

Spécification formelle

Écrivez une fonction qui, lorsqu'elle reçoit une chaîne s entièrement composée exactement de 12 chiffres décimaux (et aucun autre caractère), renvoie une chaîne t avec les propriétés suivantes:

  • t se compose exactement de 13 chiffres décimaux (et pas d'autres caractères);
  • s est un préfixe de t ;
  • la somme de tous les chiffres dans les positions impaires en t (c'est-à-dire le premier, le troisième, le cinquième, etc.), plus trois fois la somme de tous les chiffres dans les positions paires en t (c'est-à-dire le deuxième, le quatrième, le sixième, etc.), est un multiple de 10.

Exemple / cas de test

Contribution
978030640615

Sortie
9780306406157

Condition de victoire

En tant que défi de , la réponse la plus courte l'emporte.


1
L'entrée et la sortie sont-elles censées contenir des tirets ou uniquement les chiffres?
sepp2k

1
Mise à jour de la description, l'entrée et la sortie ne sont que les chiffres
Kevin Brown

La sortie de l'ISBN-13 complet est-elle également acceptable?
M. Llama

6
Notez que les questions doivent vraiment être autonomes, il serait donc utile d'inclure une description de l'algorithme ici.
FlipTack

Pour moi, le post ci-dessus est pauvre en test d'exemple ... Il y aurait enfin 10 isbn et l'un d'entre eux doit retourner 0 comme dernier chiffre ...
RosLuP

Réponses:


14

Golfscript - 25 caractères

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

La version complète du programme ne comporte que 19 caractères

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

Revenez ici pour une analyse ultérieure. En attendant, consultez ma vieille réponse sans inspiration

Golfscript - 32 caractères

Similaire au calcul du nombre luhn

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

Analyse pour 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'

Après avoir exécuté le code via un interpréteur, je pense que vous pouvez enregistrer certains caractères (dans votre solution basée sur Luhn à 32 caractères) en vous débarrassant du premier {et des trois derniers caractères; }:f. Je me demande si la même chose peut être faite pour la première solution ...
Rob

@MikeDtrick, ces caractères sont la façon dont GS définit une fonction. La version 19 caractères fait ce que vous suggérez, mais la question demandait une "fonction"
gnibbler

Oh d'accord, merci pour l'explication.
Rob

Vous n'avez pas besoin de :f(ouais, je sais que les fonctions étaient couramment nommées à l'époque).
Erik the Outgolfer

8

Python - 44 caractères

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

Python - 53 caractères

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

Je pense que f ('9780306406159') produit '97803064061598' au lieu de '9780306406157'
Eelvex

@Eelvex, la chaîne d'entrée doit toujours comporter 12 chiffres
gnibbler

Ah, en quelque sorte '9' s'est glissé. Désolé ...
Eelvex

7

Haskell - 54 caractères

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

Cela nécessite la prise en charge des compréhensions de listes parallèles , qui sont prises en charge par GHC (avec l' -XParallelListCompindicateur) et Hugs (avec l' -98indicateur).


N'avez-vous pas besoin d'inclure ce drapeau dans le décompte? En plus de cela, vous pouvez remplacer [1,3]par [9,7]et supprimer le -qui vous fait économiser un octet :)
ბიმო

7

APL (27 caractères)

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

J'utilise Dyalog APL comme interprète. Voici une explication rapide, principalement de droite à gauche (dans la définition de la fonction, F←{ ... }):

  • ⍎¨⍵: Exécute / évalue ( ) chaque ( ¨) caractère donné dans le bon argument ( ).
  • (12⍴1 3): Remodeler ( ) le vecteur 1 3en un 12vecteur -élément (répéter pour combler les lacunes).
  • +.×: Prenez le produit scalaire ( +.×) de son argument gauche ( (12⍴1 3)) et son argument droit ( ⍎¨⍵).
  • 10-: Soustrayez de 10.
  • 10|: Trouvez le reste après la division par 10.
  • : Formate le nombre (c.-à-d. Donne une représentation des caractères).
  • ⍵,: Append ( ,) notre chiffre calculé à l'argument de droite.

6

PHP - 86 85 82 caractères

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

Reformater et expliquer:

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
}

Exactement l'approche que j'ai adoptée dans ma réponse C #. Semble PHP est ~ 9 caractères plus efficace!
Nellius

6

Windows PowerShell, 57

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

5

Haskell, 78 71 66 caractères

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

5

Rubis - 73 65 caractères

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

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

@Nemo, merci, mon rubis est un peu rouillé
gnibbler

Utilisez la syntaxe Ruby 1.9 f=->s{...}. Enregistrez 6 caractères. Écrivez également s<<(...).to_sau lieu d'ajouter 48 et utilisez Fixnum#chr.
Hauleth

4

C # (94 caractères)

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

Avec les sauts de ligne / espaces pour la lisibilité:

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

Testé sur plusieurs ISBN de livres sur mon étagère, donc je sais que ça marche!


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`

Les espaces sont facultatifs entre le premier argument et for(et inet le troisième) dans une compréhension de liste tant qu'il peut être divisé par l'analyseur (sans utiliser de nom de variable). -2 caractères là-bas.
Nick T

4

Perl, 53 caractères

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

4

C # - 89 77 caractères

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

Formaté pour la lisibilité:

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

Nous ne multiplions pas par un ou trois, nous ajoutons simplement tout, et nous ajoutons une fois de plus tous les caractères pairs, multipliés par deux.

9992 est suffisamment grand pour que la somme de tous les caractères ASCII soit inférieure à celle (afin que nous puissions modifier par 10 et être sûr que le résultat est positif, pas besoin de modifier par 10 deux fois), et n'est pas divisible par zéro car nous ajoutons jusqu'à tous ces 2 * 12 * 48 supplémentaires (douze chiffres ASCII, pesés par 1 et 3) == 1152, ce qui nous permet d'épargner un caractère supplémentaire (au lieu de soustraire deux fois 48, nous soustrayons 0 juste pour convertir de char en int, mais au lieu de 990, nous devons écrire 9992).

Mais là encore, bien que beaucoup moins belle ;-), cette solution à l'ancienne nous amène à 80 caractères (mais c'est presque compatible 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'

par exemple

f '978030640615'
9780306406157

ancienne façon:

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

1
(i.12)(".@{)ypeut être remplacé par"."0 y
J Guy

3

Ruby - 80 caractères

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 caractères

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

Appelez comme lIx, par exemple:

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

3

Q, 36 caractères

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

2

D - 97 caractères

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

Format plus lisible:

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

Cependant, la verbosité de l'opérateur de conversion de D rend plus difficile l'écriture de code obsessionnellement court.


2

Java - 161 caractères :(

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

Cette réponse ne satisfait pas la première exigence, car ce n'est pas une fonction.
han

1

Q (44 caractères)

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

C'est en fait incorrect, je pense
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)

Essai:

val isbn="978030640615"
b(isbn)

Résultat:

"9780306406157"

1

C, 80 79 caractères

La fonction modifie la chaîne en place, mais renvoie le pointeur de chaîne d'origine pour répondre aux exigences du problème.

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

Quelques explications: au lieu de soustraire 48 (la valeur ASCII du chiffre 0) de chaque caractère saisi, l'accumulateur sest initialisé pour qu'il soit modulo 10 égal à 48 + 3 * 48 + 48 + 3 * 48 ... + 48 + 3 * 48 = 24 * 48 = 1152. L'étape 10-sumpeut être évitée en accumulant spar soustraction au lieu d'addition. Cependant, l'opérateur du module %en C ne donnerait pas de résultat utilisable s'il sétait négatif, donc au lieu d'utiliser s-=les multiplicateurs 3 et 1, ils sont remplacés respectivement par -3 = 7 modulo 10 et -1 = 9 modulo 10.

Harnais de test:

#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 caractères

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

utilisation:

String z = "978030640615"
println i(z)

-> 9780306406157

1

APL (25)

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

L'algo doit se terminer par 10 | sinon, il pourrait renvoyer 10 au lieu de 0
RosLuP

1

Perl 6 , 29 octets

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

Essayez-le en ligne!


Donc, la base 1 peut être utilisée comme substitut à la somme? Intéressant!
Jo King

2
@JoKing C'est en fait une très vieille astuce dans le monde d'APL et J :)
Bubbler

Mauvais résultat pour 978186197371 il semble être 8 pas 9 ...
RosLuP

Excusez-moi, je pense que j'ai collé le mauvais numéro
RosLuP

1

Python 2 , 78 76 octets

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

Essayez-le en ligne!

Prend une chaîne comme argument.

Explication:

En utilisant la notation de tranche python, convertit une chaîne en une liste de paires de caractères. ("978030640615" -> [("9", "7"), ("8", "0"), ("3", "0"), ("6", "4"), ("0 "," 6 "), (" 1 "," 5 ")])

Pour cette liste de paires, convertit chaque élément en un entier et renvoie un + 3b.

Résume tous les résultats.

Obtient la somme modulo 10, OU 10 si le reste est 0. (Cela empêche le dernier chiffre d'être 10 au lieu de 0.)

Supprime le reste de 10 pour obtenir le chiffre de contrôle.

Convertit le chiffre de contrôle calculé en une chaîne via une expression de backtick déconseillée.

Renvoie le numéro d'origine plus le chiffre de contrôle calculé.

Modifier:

Sauvé 2 byes en supprimant des espaces (merci Jo King !).


Vous pouvez supprimer les espaces avant foretor
Jo King

Résultat pour moi que 978186197371 a le dernier chiffre 8 et non 9 ... J'obtiens ce numéro du seul lien que
j'imprime

Excusez-moi, je pense avoir collé le mauvais numéro ...
RosLuP

1

APL (Dyalog Unicode) , 18 octets SBCS

Fonction de préfixe tacite anonyme prenant la chaîne comme argument. En utilisant l'approche de Bubbler .

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

Essayez-le en ligne!

 longueur de l'argument (12)

9 7⍴⍨ remodeler cycliquement [9,7]à cette longueur

+.× produit scalaire de ce qui suit avec cela:

⍎¨ `évaluer chaque personnage

10| mod-10 de cela

,∘⍕ ajouter ce qui suit la stringification de cela:

 l'argument non modifié


1

dc , 25 octets

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

Essayez-le en ligne!

Je sais qu'il y a déjà une réponse DC ici, mais 25 <44, donc je suppose que je me sens bien à 19 octets. Cela utilise le fait qui 8+9^zest équivalent à -3ou -1mod 10 selon que z est pair ou impair. J'utilise donc A~pour diviser le nombre en chiffres sur la pile, mais au fur et à mesure que je crée la pile, je multiplie chaque chiffre par 8+9^zoù z est la taille de pile actuelle. Je les ajoute ensuite à mesure que la pile de fonctions se déroule et j'imprime le dernier chiffre.


0

MATLAB - 82 caractères

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

0

R, 147 caractères

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="")}

Usage:

f("978030640615")
[1] "9780306406157"

0

J, 25

,[:":10|0(-+/)"."0*1 3$~#
   f =:, [: ": 10 | 0 (- + /)". "0 * 1 3 $ ~ #
   f '978030640615'
9780306406157
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.