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ź.


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'

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`

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ą).


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.

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
}

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}

5

Haskell, 78 71 66 znaków

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

5

Ruby - 73 65 znaków

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

"\\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!


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`

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}

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

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)&(".@{))))

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

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'


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.


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

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}

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"

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

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

1

APL (25)

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

Algo musi kończyć się na 10 | bo inaczej mógłby zwrócić 10 zamiast 0
RosLuP


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 !).


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


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ę.


0

MATLAB - 82 znaki

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

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"

0

J, 25

,[:":10|0(-+/)"."0*1 3$~#
   f =:, [: ": 10 | 0 (- + /)". "0 * 1 3 $ ~ #
   f „978030640615”
9780306406157
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.