将ISBN-13转换为ISBN-10


21

介绍

在此挑战中,您的任务是,在给定书号为ISBN-13的情况下,为书籍生成ISBN-10代码。这样的ISBN-13代码由以下几部分组成-

978-GG-PPPP-TTT-C

字母G(组),P(发布者),T(标题)和C(校验和)都代表一位数字。出于这一挑战的目的,分组和计算C(参见挑战)并不有趣,我们将删除所有连字符以简化此任务。

ISBN-10号的布局非常相似:

GG-PPPP-TTT-c

字母GPT中的相同的13位数字的ISBN,但是c是不同的(并且使用不同的算法来计算)。c选择数字的方式应使以下等价成立(数字顺序):

10*G + 9*G + 8*P + … + 3*T + 2*T + 1*c = 0 (mod 11)

让我们考虑一下ISBN编号9780345391803:要获得其相应的ISBN-10代码,我们只需删除前导978和校验和3yield 034539180

接下来,我们需要计算新的校验和:

10*0 + 9*3 + 8*4 + 7*5 + 6*3 + 5*9 + 4*1 + 3*8 + 2*0 = 185

下一个可被整除的数字11187,因此新的校验和为2,因此得到的是ISBN-10代码0345391802

规则

  • 您的输入将始终具有一个对应的ISBN-10数字(即,该数字正好是13位数字,以开头978
  • 输入不一定是有效的ISBN-13(例如9780000000002
  • 您可以确保得到的ISBN不会以 X
  • 您可以将输入作为整数或字符串(带或不带连字符),但是不允许预先计算的数字列表
  • 您的输出必须是有效的ISBN-10编号(带或不带连字符)
  • 您的输出可能是整数或字符串(同样没有数字列表)

测试用例

9780000000002 -> 0000000000
9780201882957 -> 0201882957
9781420951301 -> 1420951300
9780452284234 -> 0452284236
9781292101767 -> 1292101768
9780345391803 -> 0345391802

注意前导零!


5
它根本不会影响解决方案,但是仅仅为了成为那个家伙,您对ISBN的各个部分(-10或-13)如何分开的描述是不正确的。注册组元素的长度是可变的,后续部分的位数可以在注册组之间和内部变化。例如,在两个0-684-84328-5和中99921-58-10-7,第一部分(分别是099921)是注册组,第二部分是发布者,依此类推。
乔丹

5
10/10示例ISBN选择
Jakob

Answers:


10

视网膜 44  39 28字节

>,L3,-2`.+
.
$.>`**
_{11}

_

在线尝试!

说明

是时候炫耀一些新的Retina功能了。:)

>,L3,-2`.+

我们将整个输入与.+匹配,返回match L,但仅选择字符3(从零开始)到-2(倒数第二个)(包括两个字符)。我们还将打印结果,但不带换行符(>)。

现在,减去Retina中的东西有点烦人。但幸运的是,我们正在对11进行模运算,因此我们可以将线性组合(模数11)的系数取反并将所有内容相加。换句话说,如果约束是:

10*G + 9*G + 8*P + … + 3*T + 2*T + 1*c = 0 (mod 11)

然后我们得到:

c = 1*G + 2*G + 3*P + … + 8*T + 9*T (mod 11)

这在这里简化了很多事情:

.
$.>`**

我们在底部用那个东西替换每个字符。*是Retina的重复运算符。它是右关联的,$&在左侧和_右侧都有隐式操作数,因此替换实际上是的缩写$.>`*$&*_$&*_创建一个由d下划线组成的字符串,其中d是我们当前要替换的数字。然后$.>`是直到并包括匹配项的字符串长度。1因此,整个表达式导致n的一元表示我们线性组合个项的。

_{11}

一元运算实际上是微不足道的:我们只需删除所有完整的11个下划线。

_

最后,我们计算剩余的下划线并打印结果,从而完成ISBN-10。


1如何$.>`使字符串的长度达到并包括匹配项?您可能对$`正则表达式替换很熟悉,它为您提供了直到(但不包括)匹配项的字符串。通过插入>,我们可以将的上下文$`移至当前匹配项和下一个匹配项之间的分隔符(这是当前数字和下一个匹配项之间的空字符串)。该分隔符$`将包括当前匹配项。因此$>`是一种较短的书写方式$`$&。最后,所有的$x型替代的元素,视网膜可以让你插入一个.$拿到它的长度。


这是11模的魔法是什么?这样可以为我节省4个字节...但是我不明白!
streetster

1
@streetster基本上,-2 ≡ 9 (mod 11)(因为在同余类mod 11中,从数字中加上或减去11不会更改其“值”)。而且加法和乘法遵守同余类,因此您可以将线性组合中的任何值替换为当前模下的等效值。我之所以说负数,实际上是因为我重新排列了方程式,使其c一侧具有所有其他项(作为负数)。
马丁·恩德

我现在明白了。因此,您移动c成为-c = ...而不是乘以从中10 9 8...减去11以获得-1 -2 -3...,然后将一切乘以-1得到c
streetster

您介意解释为什么最后阶段仅替换下划线吗?我花了一段时间试图弄清楚是什么原因造成的,但是我似乎无法复制它。顺便说一下,此更新看起来很棒,干得好!
FryAmTheEggman '18

1
@FryAmTheEggman谢谢:)那时,字符串仅包含下划线。我们已经在第一阶段打印了前九位数字。
Martin Ender '18

6

05AB1E17 15 13 12字节

¦¦¦¨DSƶO11%«

在线尝试!

说明

¦¦¦            # remove the first 3 characters
   ¨           # remove the last character
    D          # duplicate
     S         # split to list of digits
      ƶ        # multiply each by its 1-based index
       O       # sum
        11%    # mod by 11
           «   # concatenate

5

PowerShell96 84字节

$y=-(([char[]]($x="$args"-replace'^978|.$')|%{--$a*[int]"$_"})-join'+'|iex)%11;$x+$y

在线尝试!

接受输入"$args",执行正则表达式-replace仅获取相关部分,并将$x其存储为字符串。然后我们将其char强制转换为-array并遍历每个字母。在循环内部,我们先递减$a(默认为0),然后根据校验和计算将其相乘。注意强制转换为int,否则将使用ASCII值。

然后,我们将-join这些数字与+并将其通过管道传输到iexInvoke-Expression并类似于eval)。我们将其%11存储到该校验和中$y。最后,我们对concatenate进行字符串化$x + $y,然后将其保留在管道中。输出是隐式的。

感谢Emigna,节省了12个字节。


我不太了解powershell,但我认为类似这样的方法可以适用于84
Emigna

@Emigna是的,当然。模数算法和我的大脑不能很好地发挥作用。
AdmBorkBork

5

八度46 41 39 37字节

@(a)[c=a(4:12) 48+mod(7+c*(1:9)',11)]

在线尝试!

该代码将输入作为字符串,并返回一个字符串。

代码分解如下:

@(a) 创建一个匿名函数。

通过[c=a(4:12) ... ]提取构成主要代码的字符,将副本保存到c以后使用,并将另一个副本添加到最终输出字符串中。

根据@ MartinEnter的交换的聪明的方式10:-1:2进入1:10,我们可以很容易地生成范围,并调换它得到的列向量。c*(1:10)'对行向量c和范围列向量进行数组乘法。这等效于进行逐元素乘法然后求和。

校验和通常是mod(11-sum,11)计算总和为11的倍数所需的数量。但是,因为c是字符串,所以总和实际上比2592(48 * 54)大,因为我们乘以数字比实际值大48。

当我们执行模时,它将自动除掉2592中的7个。除此以外,考虑到范围的取反,实际的计算将变为 48+mod(7+sum,11)。我们在结果上加上48,以转换回ASCII字符。

校验和字符将附加到结果的末尾,并返回值。


5

果冻,12字节

ṫ4ṖȮV€xJS%11

这是一个使用字符串进行I / O的完整程序。

在线尝试!

怎么运行的

ṫ4ṖȮV€xJS%11  Main link. Argument: s (string of length 13)

ṫ4            Tail 4; discard the first three characters.
  Ṗ           Pop; discard the last characters.
   Ȯ          Output; print the result to STDOUT and return it.
    V€        Eval each; turn digit characters into digits.
       J      Indices; yield [1, ..., 13].
      x       Repeat the first digit once, the second digit twice, etc.
        S%11  Take the sum, modulo 11.
              (implicit) Print the checksum to STDOUT.

4

JavaScript(ES6),59个 56字节

s=>(s=s.slice(3,-1))+[...s].reduce(n=>n+s[i++]*i,i=0)%11

-3个字节,感谢@Shaggy的建议



1
或者甚至是56个字节
粗野的

那么,为什么不输入数字数组呢?54个字节
tsh


3

Pyth,16个字节

%s.e*ksbpP>Q3hT

在这里尝试!

Pyth,17个字节

%s*VsMKpP>Q3SlK11

在这里尝试!

说明

%s.e*hksbpP>Q3hT || Full program. Uses string for input and output.

            Q    || The input.
           > 3   || With elements at indexes smaller than 3 trimmed.
          P      || Pop (remove the last item).
         p       || Print the result without a linefeed, but also return it.
  .e             || Enumerated map. For each (index, value), assign two variables (k, b).
       sb        || b converted to an integer.
    *hk          || And multiplied by k + 1.
 s               || Summation.
%                || Modulo by:
               T || The literal 10.
              h  || Incremented by 1.


3

六角77 61字节

,,,,'~'11=\.A&.=\./';"-'"{4.8}}\'.A.>.,<\'+'%!@}/=+'+{./&{{&/

在线尝试!


有色:


这是一个较大的版本。有一些交叉路口,但是由于所有这些单元格都是.(Hexagony中为无操作),因此您无需担心它们:

(我也尝试过保留旧的镜子,但有时我需要更改一些内容)

执行的线性命令为:

,,,,'48}}
,
while memory > 0:
    ';"-'"{+'+{=A&=''A
    if memory < 0:
        undefined behavior
    &{{&}
    ,
'"''+~'11='%!@

说明:该程序无需保留计数器并在每个数字上进行乘法运算:

  • 保留“部分总和”变量和“总和”变量(pt
  • 对于每个读取的数字:将其添加到部分和,然后将部分和添加到总和。
  • print (-p-t)%11%总是返回肯定的结果。

3

K(ok)29 25 24 23字节

解:

x,$11!7+/(1+!9)*x:-1_3_

在线尝试!

例子:

x,$11!7+/(1+!9)*x:-1_3_"9780000000002"
"0000000000"
x,$11!7+/(1+!9)*x:-1_3_"9780345391803"
"0345391802"
x,$11!7+/(1+!9)*x:-1_3_"9781292101767"
"1292101768"
x,$11!7+/(1+!9)*x:-1_3_"9780452284234"
"0452284236"

说明:

评估从右到左执行。

其他解决方案中的两个技巧:

  • 乘以1 2 3 ...而不是10 9 8 ...
  • 将ASCII值相乘,然后将7加到平衡

分解:

x,$11!7+/(1+!9)*x:-1_3_ / the solution
                     3_ / drop three items from the start
                  -1_   / drop one item from the end
                x:      / save this as variable x
               *        / multiply by
         (    )         / all this together
            !9          / til, !9 => 0 1 2 3 4 5 6 7 8
          1+            / add 1 => 1 2 3 4 5 6 7 8 9
      7+/               / sum (+) over (/), start from 7
   11!                  / mod by 11
  $                     / convert back to a string
x,                      / join with x

笔记:

  • -4个字节感谢Martin Enders的只需反转系数 ”魔术,就获得了
  • -1字节,感谢Tom Carpenter消除了转换为整数的需要(通过添加7和)
  • -1字节从7开始累加器

3

C(gcc), 96 95 87 86 85字节

(-1感谢ceilingcat)

*f(s,r,c,d)char*s,*r;{for(d=13;--d;s+=*++s<48)r=d>8?c=3,s:r,c-=~d**s;*s=58-c%11;s=r;}

在线尝试!

称为f(s),其中s是指向可修改字符数组的第一个元素的指针。修改输入数组,将指针返回到输入数组。




2

的ECMAScript 686 67个字节

a=>(c=a.substr(3,9))+([...c].map(v=>g+=--i*v,e=i=g=11)?(e-g%e)%e:0)

在线尝试!


感谢Arnauld的评论,从切换reducemap并删除了return关键字。


3
欢迎来到PPCG!答案必须是完整程序或可调用函数(尽管它们可能是未命名的函数),而不仅仅是片段。我相信JavaScript中最短的选项通常是未命名的lambda。
马丁·恩德

@MartinEnder谢谢,我已经编辑了答案
Kos

3
欢迎登机!一些提示:变量初始化通常可以作为的额外参数使用map()reduce()等等。通过进行一些额外的重写,通常可以摆脱{}return。另外,在这种情况下,map()可能比短reduce()。(是一个65字节的版本。)
Arnauld

我很确定f=没有必要。另外,您可以c像这样进行扩展:a=>{i=10;s=[...c=a.substr(3,9)].reduce((g,v)=>+g+(i--)*v,0)%11;return c+=s?11-s:0}
Asone Tuhid

2

视网膜0.8.272 51个字节的

...(.*).
$1¶$1
r`.\G
$&$'
r`.\G
$*
1{11}

¶(1*)
$.1

在线尝试!因为我还没学过Retina 1.0。说明:

...(.*).
$1¶$1

删除不需要的字符,然后再复制适当的数字。

r`.\G
$&$'

在第二个副本中的每个数字后缀后缀。这有效地重复了后缀中的每个数字的位置。

r`.\G
$*

将第二个副本中的数字转换为一元,从而将它们加在一起。

1{11}

减少模数11。(第一份副本中只有9位数字,因此永远不会影响它。)

¶(1*)
$.1

将结果转换回十进制并再次删除换行符。


2

APL(Dyalog Unicode)26 24字节

∊⍕¨(⊢,11|⊢+.×⍳∘≢)3↓¯1↓⍎¨

在线尝试!

隐式前缀功能。将输入作为字符串。

@ngn节省了2个字节。

怎么样?

∊⍕¨(⊢,11|⊢+.×⍳∘≢)3↓¯1↓⍎¨     Main function.
                       ⍎¨     Execute each; turns the string into a vector of digits.
                 3↓¯1        Drop (↓) the last 1) and the first 3 digits.
   (           ≢)             Tally; returns the number of digits in the vector.
             ⍳∘                Then (∘) index (⍳) from 1
            ×                 Multiply the resulting vector [1..9]
         ⊢+.                  Dot product with sum with the original vector;
                              This will multiply both vectors, and sum the resulting vector.
      11|                     Mod 11
     ,                        Concatenate
                             With the original vector
 ⍕¨                           Format each; returns a vector of digits as strings.
                             Flatten to get rid of the spaces.


1

Kotlin,83个字节

i.drop(3).dropLast(1).let{it+(11-(it.mapIndexed{i,c->(10-i)*(c-'0')}.sum()%11))%11}

美化

i.drop(3).dropLast(1).let {
    it + (11 - (it.mapIndexed { i, c -> (10 - i) * (c - '0') }.sum() % 11)) % 11
}

测试

data class Test(val input: String, val output: String)

fun f(i: String) =

i.drop(3).dropLast(1).let{it+(11-(it.mapIndexed{i,c->(10-i)*(c-'0')}.sum()%11))%11}

val tests = listOf(
        Test("9780000000002", "0000000000"),
        Test("9780201882957", "0201882957"),
        Test("9781420951301", "1420951300"),
        Test("9780452284234", "0452284236"),
        Test("9781292101767", "1292101768"),
        Test("9780345391803", "0345391802")
)

fun main(args: Array<String>) {
    for (c in tests) {
        val answer = f(c.input)
        val good = answer == c.output
        println("$good ${c.input} -> ${c.output} | $answer")
    }
}

蒂奥

在线试用



1

PHP,64字节

不幸的是,在PHP (-$c)%11中与-($c%11); 相同。因此,我必须使差值至少达到最大可能值(55 * 9 = 495 = 45 * 11),而不仅仅是使用-$c%11

for($f=11;--$f>1;print$d)$c+=$f*$d=$argn[13-$f];echo(495-$c)%11;

要么

for($c=45*$f=11;--$f>1;print$d)$c-=$f*$d=$argn[13-$f];echo$c%11;

与管道一起运行-nR在线尝试


0

Java 10,110个字节

l->{var s=l+"";int c=0,i=3;for(;i<12;)c+=(13-i)*(s.charAt(i++)-48);return(l-(long)978e10)/10*10+(11-c%11)%11;}

将输入和输出作为 long整数。在这里在线尝试。

非高尔夫版本:

l -> { // lambda taking a long as argument
    var s = l + ""; // convert the input to a String
    int c = 0, // the check digit
    i = 3; // variable for iterating over the digits
    for(; i < 12 ;) // go from the first digit past 978 to the one before the check digit
        c += (13 - i) * (s.charAt(i++) - 48); // calculate the check sum
    return (l - (long) 978e10) // remove the leading 978
           /10 *10 // remove the original check digit
           + (11 - c % 11) % 11; // add the new check digit
}
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.