Oblicz cyfrę kontrolną ISBN-13

28

Napisz funkcję, która przy pierwszych 12 cyfrach kodu ISBN-13 obliczy cały ISBN poprzez obliczenie i dodanie odpowiedniej cyfry kontrolnej.

Dane wejściowe twojej funkcji to ciąg zawierający pierwsze 12 cyfr numeru ISBN. Jego wyjściem jest ciąg znaków zawierający wszystkie 13 cyfr.

Specyfikacja formalna

Napisz funkcję, która otrzyma ciąg s składający się w całości z dokładnie 12 cyfr dziesiętnych (i żadnych innych znaków), zwraca ciąg t o następujących właściwościach:

  • t składa się z dokładnie 13 cyfr dziesiętnych (i żadnych innych znaków);
  • s jest prefiksem t ;
  • suma wszystkich cyfr w pozycjach nieparzystych wt (tj. pierwsza, trzecia, piąta itd.), plus trzykrotna suma wszystkich cyfr w pozycjach parzystych wt (tj. druga, czwarta, szósta itd.), jest liczbą wielokrotność 10.

Przykład / przypadek testowy

Wkład
978030640615

Wydajność
9780306406157

Warunek zwycięstwa

Jako wyzwanie do wygrywa najkrótsza odpowiedź.

Kevin Brown
źródło
1
Czy dane wejściowe i wyjściowe powinny zawierać myślniki, czy tylko cyfry?
sepp2k
1
Zaktualizowałem opis, dane wejściowe i wyjściowe są tylko cyframi
Kevin Brown
Czy wyjście pełnego ISBN-13 jest również dopuszczalne?
Pan Llama,
6
Należy pamiętać, że pytania powinny być naprawdę samodzielne, dlatego pomocne byłoby dołączenie opisu algorytmu tutaj.
FlipTack,
Dla mnie powyższy post nie ma przykładowego testu ... Byłoby co najmniej 10 isbn, a jeden z nich musi zwrócić 0 jako ostatnią cyfrę ...
RosLuP

Odpowiedzi:

14

Golfscript - 25 znaków

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

Cała wersja programu to tylko 19 znaków

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

Zajrzyj tu później do analizy później. Tymczasem sprawdź moją starą, nieinspirowaną odpowiedź

Golfscript - 32 znaki

Podobne do obliczania liczby Luhna

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

Analiza dla 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
źródło
Po uruchomieniu kodu przez interpretera myślę, że możesz zapisać niektóre znaki (w swoim 32-znakowym rozwiązaniu opartym na Luhn), pozbywając się pierwszego {i ostatnich trzech znaków; }:f. Zastanawiam się, czy to samo można zrobić dla pierwszego rozwiązania ...
Rob
@MikeDtrick, te znaki to sposób, w jaki GS definiuje funkcję. Wersja z 19 znakami robi to, co sugerujesz, ale pytanie zadało „funkcję”
gnibbler
No dobrze, dziękuję za wyjaśnienie.
Rob
Nie potrzebujesz :f(tak, wiem, że funkcje były wtedy powszechnie nazywane).
Erik the Outgolfer
8

Python - 44 znaki

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

Python - 53 znaki

def f(s):d=map(int,s);return s+`-sum(d+d[1::2]*2)%10`
gnibbler
źródło
Myślę, że f („9780306406159”) wyprowadza „97803064061598” zamiast „9780306406157”
Eelvex
@Eelvex, ciąg wejściowy powinien zawsze składać się z 12 cyfr
gnibbler
Ach, jakoś
wkroczyło
7

Haskell - 54 znaki

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

Wymaga to obsługi równoległego przetwarzania list , która jest obsługiwana przez GHC (z -XParallelListCompflagą) i Uściski (z -98flagą).

Joey Adams
źródło
Czy nie musisz uwzględniać tej flagi w liczeniu? Poza tym, można zastąpić [1,3]przez [9,7]i usunąć -który oszczędza bajt :)
ბიმო
7

APL (27 znaków)

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

Używam Dyalog APL jako mojego tłumacza. Oto krótkie wyjaśnienie, głównie od prawej do lewej (w ramach definicji funkcji F←{ ... }):

  • ⍎¨⍵: Wykonaj / oceń ( ) każdy ( ¨) znak podany we właściwym argumencie ( ).
  • (12⍴1 3): Przekształć ( ) wektor 1 3w wektor 12-elementowy (powtarzając, aby wypełnić luki).
  • +.×: Weź iloczyn iloczynu ( +.×) jego lewego argumentu ( (12⍴1 3)) i prawego argumentu ( ⍎¨⍵).
  • 10-: Odejmij od 10.
  • 10|: Znajdź resztę po podzieleniu według 10.
  • : Sformatuj liczbę (tzn. Podaj reprezentację znaków).
  • ⍵,: Dołącz ( ,) naszą cyfrę obliczeniową do właściwego argumentu.
Dillon Cower
źródło
6

PHP - 86 85 82 znaków

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

Ponowne formatowanie i objaśnienie:

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ý
źródło
Dokładnie takie podejście zastosowałem w mojej odpowiedzi w języku C #. Wydaje się, że PHP jest ~ 9 znaków bardziej wydajny!
Nellius
6

Windows PowerShell, 57

filter i{$_+(990-($_-replace'(.)(.)','+$1+3*$2'|iex))%10}
Joey
źródło
5

Haskell, 78 71 66 znaków

i s=s++(show$mod(2-sum(zipWith(*)(cycle[1,3])(map fromEnum s)))10)
sepp2k
źródło
5

Ruby - 73 65 znaków

f=->s{s+((2-(s+s.gsub(/.(.)/,'\1')*2).bytes.inject(:+))%10).to_s}
gnibbler
źródło
"\\1"-> '\1'?
Nemo157
@Nemo, dzięki, mój rubin jest trochę zardzewiały
gnibbler
Użyj składni Ruby 1.9 f=->s{...}. Zaoszczędź 6 znaków. Napisz także s<<(...).to_szamiast dodawać 48 i użyj Fixnum#chr.
Hauleth,
4

C # (94 znaków)

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

Z podziałami / białymi znakami dla czytelności:

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

Testowane na kilku numerach ISBN z książek na mojej półce, więc wiem, że to działa!

Nellius
źródło
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
źródło
Spacje są opcjonalne między pierwszym argumentem a for(i intrzecim) w interpretacji listy, o ile można je podzielić przez analizator składni (nie używając nazwy zmiennej). -2 znaki tam.
Nick T
4

Perl, 53 znaki

sub i{$_=shift;s/(.)(.)/$s+=$1+$2*3/ge;$_.(10-$s)%10}
ninjalj
źródło
4

C # - 89 77 znaków

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

Sformatowane dla czytelności:

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

Nie mnożymy przez jeden lub trzy, po prostu dodajemy wszystko, a dodatkowo dodajemy wszystkie znaki o równej pozycji jeszcze raz, pomnożone przez dwa.

9992 jest wystarczająco duży, aby suma wszystkich znaków ASCII była mniejsza (abyśmy mogli modyfikować o 10 i mieć pewność, że wynik jest dodatni, nie trzeba dwukrotnie modyfikować o 10), i nie można go podzielić przez zero, ponieważ dodajemy wszystkie te dodatkowe 2 * 12 * 48 (dwanaście cyfr ASCII, ważonych 1 i 3) == 1152, co pozwala nam zaoszczędzić jeden dodatkowy znak (zamiast dwukrotnego odejmowania 48, odejmujemy 0 tylko w celu konwersji z char na int, ale zamiast 990 musimy napisać 9992).

Ale z drugiej strony, choć znacznie mniej piękne ;-), to oldschoolowe rozwiązanie zapewnia nam 80 znaków (ale jest to prawie kompatybilne z 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
źródło
4

J - 55 45 38

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

na przykład

f '978030640615'
9780306406157

stara droga:

f=:,":@(10(10&|@-)(10&|@+/@((12$1 3)*(i.12)&(".@{))))
Eelvex
źródło
1
(i.12)(".@{)ymożna zastąpić"."0 y
J Guy
3

Ruby - 80 znaków

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

dc, 44 znaki

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

Wywołaj jako lIxnp .:

dc -e'[d0r[I~3*rI~rsn++lndZ0<x]dsxx+I%Ir-I%rI*+]sI' -e '978030640615lIxp'
ninjalj
źródło
3

Q, 36 znaków

{x,-3!10-mod[;10]sum(12#1 3)*"I"$'x}
tartin
źródło
2

D - 97 znaków

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

Bardziej czytelnie sformatowany:

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

Szczegółowość operatora rzutowania D zdecydowanie utrudnia jednak pisanie obsesyjnie krótkiego kodu.

Jonathan M. Davis
źródło
2

Java - 161 znaków :(

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));
Oktawian A. Damiean
źródło
Ta odpowiedź nie spełnia pierwszego wymogu, ponieważ nie jest funkcją.
han
1

Q (44 znaki)

f:{x,string 10-mod[;10]0+/sum@'2 cut"I"$/:x}
skeevey
źródło
Myślę, że to właściwie niepoprawne
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)

Testowanie:

val isbn="978030640615"
b(isbn)

Wynik:

"9780306406157"
nieznany użytkownik
źródło
1

C, 80 79 znaków

Funkcja modyfikuje ciąg w miejscu, ale zwraca oryginalny wskaźnik ciągu, aby spełnić wymagania dotyczące problemu.

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

Kilka wyjaśnień: Zamiast odejmować 48 (wartość cyfry ASCII 0) od każdego znaku wejściowego, akumulator sjest inicjalizowany, tak że ma moduł 10 równy 48 + 3 * 48 + 48 + 3 * 48 ... + 48 + 3 * 48 = 24 * 48 = 1152. Kroku 10-summożna uniknąć, gromadząc sprzez odejmowanie zamiast dodawania. Jednak operator modułu %w C nie dałby użytecznego wyniku, gdyby sbył ujemny, więc zamiast używać s-=mnożników 3 i 1 są zastępowane odpowiednio przez -3 = 7 modulo 10 i -1 = 9 modulo 10.

Uprząż testowa:

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

Groovy 75 , 66 znaków

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

posługiwać się:

String z = "978030640615"
println i(z)

-> 9780306406157
Armand
źródło
1

APL (25)

{⍵,⍕10-10|+/(⍎¨⍵)×12⍴1,3}
marinus
źródło
Algo musi kończyć się na 10 | bo inaczej mógłby zwrócić 10 zamiast 0
RosLuP
1

Perl 6 , 29 bajtów

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

Wypróbuj online!

nwellnhof
źródło
Więc podstawa 1 może być używana jako zamiennik sumy? Ciekawy!
Jo King
2
@JoKing To właściwie bardzo stara sztuczka w świecie APL i J :)
Bubbler
Błędny wynik dla 978186197371 wydaje się być 8, a nie 9 ...
RosLuP
Przepraszam, myślę, że
wkleiłem
1

Python 2 , 78 76 bajtów

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

Wypróbuj online!

Bierze ciąg jako argument.

Wyjaśnienie:

Za pomocą notacji wycinka python konwertuje ciąg znaków na listę par znaków. („978030640615” -> [(„9”, „7”), („8”, „0”), („3”, „0”), („6”, „4”), („0 „,„ 6 ”), („ 1 ”,„ 5 ”)])

Dla tej listy par konwertuje każdy element na liczbę całkowitą i zwraca wartość + 3b.

Podsumowuje wszystkie wyniki.

Pobiera sumę modulo 10, LUB 10, jeśli reszta to 0. (Zapobiega to końcowej cyfrze równej 10 zamiast 0.)

Usuwa resztę z 10, aby uzyskać cyfrę kontrolną.

Konwertuje obliczoną cyfrę kontrolną na ciąg znaków za pomocą przestarzałego wyrażenia wstecznego.

Zwraca oryginalną liczbę plus obliczoną cyfrę kontrolną.

Edytować:

Zaoszczędzono 2 bajki, usuwając spacje (dzięki Jo King !).

Triggernometria
źródło
Możesz usunąć spacje przed forior
Jo King
Rezultat dla mnie, że 978186197371 ma ostatnią cyfrę 8, a nie 9 ... Otrzymuję ten numer z jedynego łącza, które
drukuję
Przepraszam, myślę, że
wkleiłem
1

APL (Dyalog Unicode) , 18 bajtów SBCS

Anonimowa funkcja prefiksu ukrytego, przyjmująca ciąg znaków jako argument. Korzystanie z podejścia Bubblera .

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

Wypróbuj online!

 długość argumentu (12)

9 7⍴⍨ cyklicznie przekształcaj się [9,7]do tej długości

+.× iloczyn następujących produktów:

⍎¨ `oceń każdą postać

10| mod-10 tego

,∘⍕ poprzedzić następujące jego uszeregowanie:

 niezmodyfikowany argument

Adám
źródło
1

dc , 25 bajtów

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

Wypróbuj online!

Wiem, że jest tutaj odpowiedź dc, ale 25 <44, więc myślę, że czuję się z tym 19 bajtów. Ta wykorzystuje fakt, że 8+9^zjest równoważna albo -3czy -1mod 10 w zależności od tego, czy z jest parzyste, czy nieparzyste. Używam więc A~do dzielenia liczby na cyfry na stosie, ale gdy buduję stos, mnożę każdą cyfrę przez 8+9^zgdzie z oznacza bieżący rozmiar stosu. Następnie dodaję je wszystkie w miarę rozwijania się stosu funkcji i wypisuję ostatnią cyfrę.

Sophia Lechner
źródło
0

MATLAB - 82 znaki

function c(i)
[i num2str(mod(10-mod(sum(str2num(i(:)).*repmat([1;3],6,1)),10),10))]
Nowicjusz
źródło
0

R, 147 znaków

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

Stosowanie:

f("978030640615")
[1] "9780306406157"
Paolo
źródło
0

J, 25

,[:":10|0(-+/)"."0*1 3$~#
   f =:, [: ": 10 | 0 (- + /)". "0 * 1 3 $ ~ #
   f „978030640615”
9780306406157
efemeryczny
źródło