计算ISBN-13校验位


28

编写一个函数,给定ISBN-13代码的前12位,该函数将通过计算并附加适当的校验位来计算整个ISBN。

函数的输入是一个字符串,其中包含ISBN的前12位数字。它的输出是一个包含所有13位数字的字符串。

正式规格

编写一个函数,当给定一个完全由12个十进制数字组成的字符串s(并且没有其他字符)时,该函数返回具有以下属性的字符串t

  • t完全由13个十进制数字组成(没有其他字符);
  • st的前缀;
  • t中奇数位置(即第一,第三,第五等)的所有数字的总和,加上t中偶数位置(即第二,第四,第六等)的所有数字的总和的三倍。10的倍数。

示例/测试用例

输入值
978030640615

输出量
9780306406157

胜利条件

作为对挑战,最短的答案胜出。


1
输入和输出应该包含破折号还是仅包含数字?
sepp2k 2011年

1
更新了描述,输入和输出只是数字
Kevin Brown

输出完整的ISBN-13是否也可以接受?
拉玛先生先生2012年

6
请注意,问题实际上应该完全包含在内,因此在此处包括对算法的描述将很有帮助。
FlipTack

对我来说,上面的示例测试很差……最后会有10个isbn,其中一个必须返回0作为最后一个数字……
RosLuP

Answers:


14

Golfscript-25个字符

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

整个程序版本只有19个字符

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

请稍后返回此处进行分析。同时查看我以前没有灵感的答案

Golfscript-32个字符

类似于luhn数计算

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

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'

通过解释器运行代码后,我认为您可以省去一些字符(在基于Luhn的32个字符的解决方案中),而不必去掉{前三个字符。}:f。我想知道第一个解决方案是否可以做同样的事情?
Rob

@MikeDtrick,这些字符是GS定义功能的方式。19个字符的版本可以完成您的建议,但该问题要求“功能”
gnibbler

哦,好的,谢谢您的解释。
罗布(Rob)2012年

您不需要:f(是的,我知道函数通常在那时被命名)。
Erik the Outgolfer

8

Python-44个字符

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

Python-53个字符

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

我认为,F( '9780306406159')输出'97803064061598',而不是'9780306406157'
Eelvex

@Eelvex,输入字符串应该总是12位数字
gnibbler

啊,不知何故'9'溜进来了。对不起...
Eelvex

7

Haskell-54个字符

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

这需要对并行列表推导的支持,GHC(带有-XParallelListComp标志)和Hugs(带有-98标志)都支持它。


您是否不需要在计数中包含该标志?除此之外,你可以取代[1,3]通过[9,7]并去除-这样可以节省一个字节:)
ბიმო

7

APL(27个字符)

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

我正在使用Dyalog APL作为我的解释器。这是一个快速解释,主要是从右到左(在函数定义内F←{ ... }):

  • ⍎¨⍵:执行/评估(¨右参数()中给出的每个()字符。
  • (12⍴1 3)将向量整形()1 312-element向量(重复填补空白)。
  • +.×+.×取其左参数((12⍴1 3))和右参数(⍎¨⍵)的点积()。
  • 10-:从10减去。
  • 10|:除以后的余数10
  • :格式化数字(即,使用字符表示)。
  • ⍵,:将(,)我们计算出的数字附加到正确的参数上。

6

PHP- 86 85 82个字符

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

重新格式化并说明:

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
}

正是我在C#答案中采用的方法。似乎PHP的效率提高了约9个字符!
Nellius 2011年

6

Windows PowerShell,57

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

5

Haskell,78 71 66个字符

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

5

红宝石-73 65个字符

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

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

@Nemo,谢谢,我的红宝石有点生锈
gnibbler 2011年

使用Ruby 1.9语法f=->s{...}。节省6个字符。还要写s<<(...).to_s而不是加48并使用Fixnum#chr
Hauleth 2012年

4

C#(94个字符)

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

使用换行符/空格以提高可读性:

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

在我书架上的书中对多个ISBN进行了测试,因此我知道它可以正常工作!


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`

列表解析中的第一个参数与for(和in第三个参数)之间的空格是可选的,只要它可以被解析器分割(不使用变量名)即可。-2个字符。
尼克T

4

Perl,53个字符

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

4

C#– 89 77个字符

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

格式化以提高可读性:

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

我们不会乘以一或三,而是将所有内容相加,再加上所有偶数个字符再乘以二。

9992足够大,因此所有ASCII字符的总和都小于该值(以便我们可以乘以10,并确保结果为正,不需要乘以10两次),并且不能被零整除,因为我们将将所有这些额外的2 * 12 * 48(十二个ASCII数字,分别由1和3加权)== 1152,这使我们可以保留一个额外的字符(而不是两次减去48,我们减去0只是为了将char转换为int,但我们需要编写9992),而不是990。

但是再说一次,即使漂亮得多;-),这个老式的解决方案也使我们可以使用80个字符(但这几乎与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'

例如

f '978030640615'
9780306406157

旧方法:

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

1
(i.12)(".@{)y可以替换为"."0 y
J Guy

3

Ruby-80个字符

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个字符

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

调用为lIx,例如:

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

3

Q,36个字符

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

2

D-97个字符

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

格式更清晰:

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

D的强制转换运算符的冗长性无疑使编写简短的强迫性代码变得更加困难。


2

Java-161个字符 :(

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

该答案不是功能,因此不满足第一个要求。
2012年


1

斯卡拉84

def b(i:String)=i+(10-((i.sliding(2,2).map(_.toInt).map(k=>k/10+k%10*3).sum)%10)%10)

测试:

val isbn="978030640615"
b(isbn)

结果:

"9780306406157"

1

C,80 79个字符

该函数在适当的位置修改字符串,但返回原始的字符串指针以满足问题要求。

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

一些解释:而不是0从每个输入字符中减去48(数字的ASCII值),而是对累加器s进行初始化,以使其模10等于48 + 3 * 48 + 48 + 3 * 48 ... + 48 + 3 * 48 = 24 * 48 =1152。10-sum可以s通过减法而不是加法累加来避免该步骤。但是,模块运算符%如果C中不能给出可用的结果s为负数,,因此请不要使用s-= -3 = 7模10和-1 = 9模10了乘法器3和1。

测试线束:

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

1

常规75,66个字符

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

使用:

String z = "978030640615"
println i(z)

-> 9780306406157

1

APL(25)

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

算法必须以10 |结尾 因为否则它可能会返回10代替0
RosLuP


1

Python 2中78 76个字节

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

在线尝试!

以字符串作为参数。

说明:

使用python slice表示法,可将字符串转换为字符对列表。(“ 978030640615”-> [(“ 9”,“ 7”),(“ 8”,“ 0”),(“ 3”,“ 0”),(“ 6”,“ 4”),(“ 0 “,” 6“),(” 1“,” 5“)])

对于该对列表,将每个项目转换为整数并返回a + 3b。

对所有结果求和。

获取模数总和10,如果余数为0,则为10。(这将防止最终数字为10而不是0。)

从10中除去余数以得到校验位。

通过不推荐使用的反引号表达式将计算的校验位转换为字符串。

返回原始编号加上计算出的校验位。

编辑:

通过删除空格节省了2个再见(感谢Jo King!)。


您可以删除之前for和之后的空格or
Jo King

结果对我来说978186197371的最后一位数字是8,而不是9 ...我从我在Apl解决方案中打印的唯一链接中获得了该数字
RosLuP

对不起,我想我粘贴了错误的数字...
RosLuP

1

APL(Dyalog Unicode),18 字节SBCS

以字符串为参数的匿名默认前缀函数。使用冒泡者的方法

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

在线尝试!

 参数长度(12)

9 7⍴⍨ 周期性地重塑[9,7]到该长度

+.× 以下的点积:

⍎¨ `评估每个字符

10| 那的mod-10

,∘⍕ 在此之前进行以下分类:

 未经修改的论点


1

dc,25个字节

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

在线尝试!

我知道这里已经有一个直流答案,但是25 <44,所以我想我对此有19个字节的把握。根据z是偶数还是奇数,使用了8+9^z等于-3或等于-1mod 10 的事实。因此,我通常将A~数字分解为堆栈中的数字,但是在构建堆栈时,我将每个数字乘以8+9^zz是当前堆栈大小。然后在函数堆栈展开时将它们全部添加,并打印最后一位。


0

MATLAB-82个字符

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

0

R,147个字符

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

用法:

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

0

J,25

,[:":10|0(-+/)"."0*1 3$~#
   f =:,[:“:10 | 0(-+ /)”。“ 0 * 1 3 $〜#
   f'978030640615'
9780306406157
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.