อัลกอริทึม Luhn สำหรับตรวจสอบหมายเลขบัตรเครดิต ฯลฯ


49

ท้าทาย

เขียนโปรแกรมหรือฟังก์ชั่นที่สั้นที่สุดเพื่อคำนวณหาอัลกอรึทึม Luhnสำหรับการตรวจสอบหมายเลข (บัตรเครดิต)

อัลกอริทึม Luhn อธิบาย

จาก RosettaCodeอัลกอริทึมนี้สำหรับวัตถุประสงค์ของการท้าทายนี้จะถูกระบุเช่นนี้โดยมีตัวอย่างอินพุต49927398716:

Reverse the digits, make an array:
    6, 1, 7, 8, 9, 3, 7, 2, 9, 9, 4
Double the numbers in odd indexes:
    6, 2, 7, 16, 9, 6, 7, 4, 9, 18, 4
Sum the digits in each number:
    6, 2, 7, 7, 9, 6, 7, 4, 9, 9, 4
Sum all of the numbers:
    6 + 2 + 7 + 7 + 9 + 6 + 7 + 4 + 9 + 9 + 4 = 70
If the sum modulo 10 is 0, then the number is valid:
    70 % 10 = 0 => valid

กฎของ IO

อินพุต : สตริงหรือตัวเลข (ตัวเลือกของคุณ) ในรูปแบบอินพุต / เอาต์พุตภาษาที่คุณเลือก

เอาท์พุท : ค่าจริงหรือเท็จตามลำดับระบุว่าอินพุตถูกต้องตามการทดสอบข้างต้น

หมายเหตุ / เคล็ดลับ

  • พยายามอย่าโพสต์หมายเลขบัตรเครดิตหรือบัญชีของคุณโดยไม่ตั้งใจหากคุณใช้เพื่อทดสอบ :)

  • หากอินพุตไม่ถูกต้องและเป็นไปไม่ได้ที่จะประมวลผลด้วยอัลกอริทึมที่ระบุ (เช่นสั้นเกินไปที่จะทำงานด้วย) คุณสามารถทำสิ่งที่คุณต้องการรวมถึงระเบิดคอมพิวเตอร์ของฉัน

  • อย่างไรก็ตามสัญลักษณ์แสดงหัวข้อก่อนหน้าไม่ได้หมายความว่าภาษาของคุณสามารถทำทุกอย่างที่ต้องการด้วยหมายเลขที่ใหญ่เกินไปสำหรับจัดการ หากภาษาของคุณไม่สามารถจัดการกรณีทดสอบได้ให้ลองพิจารณาใช้สตริงเป็นอินพุต

ตัวอย่าง

ตัวอย่างต่อไปนี้ได้รับการตรวจสอบแล้วกับสคริปต์ Python นี้ ; หากคุณคิดว่ามีข้อผิดพลาดหรือมีคำถามเพียงแค่ ping @cat

49927398716      True
49927398717      False
1234567812345670 True    
1234567812345678 False
79927398710      False
79927398711      False
79927398712      False
79927398713      True
79927398714      False
79927398715      False
79927398716      False
79927398717      False
79927398718      False
79927398719      False
374652346956782346957823694857692364857368475368 True
374652346956782346957823694857692364857387456834 False
8 False **
0 True  **

** ตามการใช้งาน Python แต่คุณอาจทำอะไรก็ได้เพราะสิ่งเหล่านี้สั้นเกินไปที่จะมีสิทธิ์ได้รับการปฏิบัติตามข้อกำหนดอย่างเคร่งครัด


หากคำตอบใด ๆ ข้างต้นเป็นโมฆะคำตอบที่มีอยู่ (แม้ว่าฉันเชื่อว่าไม่ควรทำ) คำตอบเหล่านั้นจะไม่ถูกต้อง อย่างไรก็ตามคำตอบใหม่เพื่อให้ถูกต้องควรปฏิบัติตามข้อกำหนดข้างต้น

ลีดเดอร์บอร์ด

คำตอบ:


21

Golfscript - 24 ตัวอักษร

-1%{2+0!:0)*109%+}*10%8=

คำอธิบาย:

  1. -1% ฝืนสตริง
  2. {เริ่มบล็อก (ซึ่งเราใช้เป็นวง) อักขระแต่ละตัวในสตริงถูกส่งเนื่องจากเป็นค่า ascii
    1. 2+ เพิ่ม 2 (ค่า ascii ของหลักคือ 48 + n ดังนั้นเรามี 50 + n ตอนนี้และหลักสุดท้ายคือ n)
    2. 0!:0 กลับค่าเป็น 0 และเก็บไว้ (ทุกอย่างเป็นตัวแปร) ดังนั้นเราจึงมี 1 ในการวนซ้ำครั้งแรก, 0 ในครั้งที่สอง ฯลฯ
    3. )* บวกหนึ่งเข้ากับค่านี้และคูณมันดังนั้นเราจึงคูณด้วย 2, จากนั้น 1, จากนั้น 2, เป็นต้น
    4. 109% เหลือเศษโมดูโล 109 ซึ่งจะมีผลเฉพาะค่า 5-9 ซึ่งเพิ่มเป็นสองเท่าและลดลงเป็นค่าที่ถูกต้อง
    5. + เพิ่มค่านี้ให้กับผลรวมสะสม
  3. }*จบบล็อกและดำเนินการ 'พับ' ครั้งแรกตัวอักษรตัวแรกถูกผลัก (ตั้งแต่เรากลับด้านนี่เป็นตัวเลขตรวจสอบ) จากนั้นให้กดและดำเนินการบล็อกอีกครั้ง ดังนั้นเราจึงใช้ค่า ascii ของอักขระตัวแรกเป็นค่าเริ่มต้นสำหรับผลรวมสะสม
  4. 10% ใช้เวลาที่เหลือแบบโมดูโล 10
  5. 8= จะส่งคืน 1 ถ้าค่าเท่ากับ 8 เราใช้สิ่งนี้เพราะเราไม่ได้ทำให้อักขระที่ผลักดันตัวแรก (ตัวเลขเช็ค)

บางคนอาจคิดว่าเราสามารถใช้8-แทน2+การบันทึกตัวละครโดยการเปลี่ยน109%เป็น89%ยกเว้นแล้วเราจะต้องเพิ่มช่องว่างเพื่อให้การ-ลบ (แทน-0)


11

GolfScript, 44 ตัวอักษร

-1%{16%}%2/1,\+{(\.{0=2*.9>9*-+}{;}if+}*10%!

ความเห็นที่เลือก

ที่น่าสนใจสองรายการแรกด้านล่างแสดงให้เห็นถึงการใช้งานที่แตกต่างกันสามอย่างของ%ผู้ดำเนินการ: การเลือกอาร์เรย์แผนที่และตัวดัดแปลง ผู้ประกอบการ GolfScript ส่วนใหญ่นั้น "มีความอ่อนไหวตามบริบท" ซึ่งทำให้พวกเขามีพฤติกรรมที่แตกต่างกันอย่างมากขึ้นอยู่กับประเภทของข้อโต้แย้ง

  1. -1%ฝืนสตริง นี่เป็นสิ่งสำคัญเนื่องจากมีการนับคู่หลักจากด้านขวา
  2. {16%}% แปลงตัวเลข ASCII ทั้งหมดให้เป็นตัวเลขโดยการดัดแปลงด้วย 16
  3. 2/ แยกอาร์เรย์ออกเป็นกลุ่มละ 2
  4. 1,[0]เป็นวิธีที่ถูกที่ต้องทำ
  5. \+ผนวก 0 กับอาเรย์หลักอย่างมีประสิทธิภาพ มันทำได้โดยการสลับแล้วเชื่อมต่อกัน

0 ถูกเตรียมไว้เพื่อเตรียมพร้อมสำหรับการพับที่จะมาในครั้งต่อไป แทนที่จะใช้ค่าเริ่มต้นอย่างชัดเจนการพับของ GolfScript ใช้รายการแรกในอาร์เรย์เป็นค่าเริ่มต้น

ทีนี้ลองดูฟังก์ชั่นการพับจริง ฟังก์ชั่นนี้ใช้เวลาสองข้อโต้แย้ง: ค่าที่พับและรายการปัจจุบันในอาร์เรย์ (ซึ่งในกรณีนี้จะเป็นอาร์เรย์ 2 หรือ (ผิดปกติ) 1 เพราะ2/ก่อนหน้านี้) 1 [2 3]สมมติว่าข้อโต้แย้งที่มี

  1. (\.แยกอิลิเมนต์อาร์เรย์ซ้ายสุดออกและย้ายอาเรย์ที่เหลือไปยังด้านหน้าจากนั้นคัดลอก 1 2 [3] [3]สแต็คในขณะนี้ดูเหมือนว่า:
  2. การifตรวจสอบว่าอาร์เรย์ว่างเปล่า (ซึ่งเป็นกรณีของกลุ่มสุดท้ายเมื่อจัดการกับหมายเลขบัญชีที่มีขนาดคี่) ถ้าเป็นเช่นนั้นจะไม่มีการประมวลผลพิเศษเกิดขึ้น (เพิ่งเปิดอาร์เรย์ว่าง)
  3. สำหรับกลุ่มคู่:
    1. 0= คว้าองค์ประกอบแรก (เท่านั้นในกรณีนี้) ของอาร์เรย์ 1 2 3
    2. 2* เพิ่มจำนวนเป็นสองเท่า 1 2 6
    3. .9>9*- ลบ 9 จากตัวเลขหากมากกว่า 9 นำไปใช้เป็น: คัดลอกตัวเลขเปรียบเทียบกับ 9 คูณผลลัพธ์ (ซึ่งคือ 0 หรือ 1) ด้วย 9 แล้วลบออก 1 2 6
    4. + ในที่สุดก็เพิ่มเข้าไปในจำนวนแรก 1 8
  4. +(หลังจากนั้นif) เพิ่มผลลัพธ์ของifค่าเดิมไปเป็นผลลัพธ์ในค่าการพับใหม่

หลังจากการพับเสร็จสิ้นเราเพียงแค่ปรับ 10 ( 10%) และลบล้างผลลัพธ์ ( !) เพื่อให้เราคืน 1 iff ผลรวมจะเท่ากับ 10


ดูเหมือนว่าจะส่งคืน 0 สำหรับหมายเลขตัวอย่างบนวิกิพีเดีย (49927398716)
gnibbler

นาโนเมตร ฉันลืมใช้echo -n
gnibbler

1
@gnibbler: ฮ่าฮ่าล้มเหลว :-P (อย่างจริงจังฉันได้รับการต่อยโดยหนึ่งในการทดสอบครั้งแรกของฉัน)
Chris Jester-Young

1
สถานที่เล็ก ๆ น้อย ๆ สำหรับบันทึกตัวละครง่ายๆที่นี่ สามารถรวมกันเป็น-1% 2/ สามารถถูกแทนที่ด้วย(0 ถูกบังคับให้เป็นอาร์เรย์จากนั้นจะต่อกัน) สามารถถูกแทนที่ด้วย(เนื่องจากเราเกี่ยวข้องเฉพาะกับหลักสุดท้าย) นอกจากนี้ยังมีการตรวจสอบความยาวคี่เป็นบิตนานใช้สั้น หลังจากทำเช่นนี้เรายังสามารถเปลี่ยนและเข้า(ภายในวง) เมื่อคุณได้ทำทุกอย่างที่มันจะมีลักษณะบางอย่างเช่นนี้ -2/1,0+9>9*-9>+.,2%,\+{16%}%(\0={16}/.,2%,\+-2/0\+{{16%}/2*.9>+++}*10%!
Nabb

@Nabb: ขอบคุณ! ฉันจะรวมสิ่งเหล่านั้นไว้ในโซลูชันของฉันแม้ว่ามันจะดูเหมือนว่าคุณมีอยู่แล้วที่เตะลาจริงจัง :-)
Chris Jester-Young

11

Python 73 73ตัวอักษร

def P(x):D=map(int,x);return sum(D+[d-d/5*9for d in D[-2::-2]])%10==0

4
คุณสามารถบันทึกอีกสองตัวอักษรโดยไม่วนซ้ำไปข้างหลัง: D[-2::-2]-> D[1::2]เนื่องจากลำดับของผลรวมนั้นไม่สำคัญ :)
ThinkChaos

==0สามารถย่อให้เหลือ<1
Black Owl Kai


9

C # 119 ตัวอักษร:

bool l(string n){return(String.Join("",n.Reverse().Select((x,i)=>(x-48)*(i%2<1?1:2)+"").ToArray()).Sum(x=>x-48))%10<1;}

ไม่เลวเกินไปสำหรับโค้ดกอล์ฟ n00b ในภาษาที่พิมพ์แบบคงที่ฉันหวังว่า

สามารถลดได้ถึง100 :

bool l(string n){return String.Join("",n.Reverse().Select((x,i)=>(x-48)*(i%2+1))).Sum(x=>x+2)%10<1;}

มันเป็นความคิดที่ดีและวิธีการที่น่าสนใจ แต่มันก็ไม่ได้ผล อย่างน้อยไม่ได้มีการทดสอบน้อยของฉัน ดูเหมือนว่า "i" ในแลมบ์ดาแรกของคุณควรจะเป็นดัชนีของตัวละครในสตริง ทำงานได้ตามที่ควรหรือไม่ ถ้าเป็นเช่นนั้นเหตุใดคุณจึงย้อนกลับสตริงเพื่อปรับเปลี่ยนตามตำแหน่งดัชนี ดูเหมือนจะซ้ำซ้อนเล็กน้อยใช่ไหม?
Nellius

ฉันทดสอบบัตรเครดิตของฉันเพียงใบเดียวและผิดพลาดเพียงเล็กน้อยจาก TBH (การใช้ดีบักเกอร์ VS 2008) อัลกอริธึมควรเพิ่มเป็นสองเท่าทุกวินาทีที่เริ่มต้นด้วยหลักสุดท้าย ถ้าฉันไม่ย้อนกลับสตริงมันจะไม่ถูกต้องสำหรับสตริงที่มีความยาวคี่
mootinator

ปรากฎว่าฉันมีผลi%2<1?1:2ย้อนหลัง ขอบคุณ
mootinator

8

Golfscript - 34 ตัวอักษร

{15&}%.-2%\);-2%{.+(9%)}%+{+}*10%!

ตัวอย่างหมายเลขจากวิกิพีเดียหน้า 4992739871

{15&}%  does a bitwise and of each ascii digit with 00001111
        now I have a list of digits 
        [4 9 9 2 7 3 9 8 7 1 6]
.       makes a copy of the list, now I have two identical lists
        [4 9 9 2 7 3 9 8 7 1 6] [4 9 9 2 7 3 9 8 7 1 6]
-2%     like [::-2] in python takes every second element in reverse
        [4 9 9 2 7 3 9 8 7 1 6] [6 7 9 7 9 4]
\       swap the two lists around
        [6 7 9 7 9 4] [4 9 9 2 7 3 9 8 7 1 6]
);      drop the last digit off the list
        [6 7 9 7 9 4] [4 9 9 2 7 3 9 8 7 1]
-2%     same as before
        [6 7 9 7 9 4] [1 8 3 2 9]
{       for each item in the list ...
.+      ... double it ...
(       ... subtract 1 ...
9%      ... mod 9 ...
)}%     ... add 1 ...
        [6 7 9 7 9 4] [2 7 6 4 9]
+       join the two lists
        [6 7 9 7 9 4 2 7 6 4 9]
{+}*    add the elements up
        70
10%     mod 10
        0
!       invert the result
        1

นี่.+(9%)เป็นนวัตกรรมใหม่มาก (สำหรับฉันแล้ว) ฉันชอบ! +1
Chris Jester-Young

แต่ทว่า GolfScript จำเป็นต้องมีผู้ดำเนินการแบ่งพาร์ติชันดังนั้นคุณจึงไม่จำเป็นต้องทำเรื่อง "ดร็อปเอนด์ออกและทำซ้ำ" เรื่องไร้สาระนี้ :-)
Chris Jester-Young

1
@ Chris ฉันได้เรียนรู้เกี่ยวกับหลายปีที่ผ่านมาเรียกว่า มันเป็นวิธีที่เรียบร้อยในการตรวจสอบการเพิ่มและการคูณยาว ๆ อีกครั้ง
gnibbler

3
สิ่งนี้จะไม่ทำงานสำหรับค่า 0 ที่เพิ่มเป็นสองเท่า ( 0(9%)คือ 9 และไม่ใช่ 0)
Nabb


7

Ruby - 85 ตัวอักษร

def f s
l=s.size
s.chars.map{|k|(i=k.to_i*((l-=1)%2+1))%10+i/10}.inject(:+)%10==0
end

คุณอาจรู้เกี่ยวกับเรื่องนี้ แต่คุณสามารถทำ. ผลรวมแทน. inject (: +) เพื่อบันทึก 7 ไบต์
HåvardNygård

7

Haskell, 96 ไบต์

จะต้องมีวิธีที่ดีกว่า / สั้นกว่า แต่นี่คือโซลูชันHaskellของฉันใน96 ตัวอักษร :

l=(==0).(`mod`10).sum.zipWith($)(cycle[id,\x->x`mod`5*2+x`div`5]).reverse.map((+(-48)).fromEnum)

น่าเศร้าที่digitToIntฟังก์ชั่นนี้สามารถใช้งานได้เมื่อคุณimport Data.Charเป็นคนแรกเท่านั้น มิฉะนั้นฉันจะได้รับลงไป 88 ตัวอักษรโดยการแทนที่ด้วย((+(-48)).fromEnum)digitToInt


6

Windows PowerShell, 82

filter f{!((''+($_[($_.length)..0]|%{+"$_"*($i++%2+1)})-replace'.','+$&'|iex)%10)}

ประวัติความเป็นมา:

  • 2011-02-13 03:08 (84) ความพยายามครั้งแรก
  • 2011-02-13 12:13 (82) ฉันไม่จำเป็นต้องเข้าร่วมเพราะช่องว่างไม่เจ็บ +1 + +3ยังสามารถประเมินได้

5

คำถามที่ 63

{0=mod[(+/)"I"$(,/)($)($)@["I"$'x;1+2*'(!)(_)((#)x)%2;*;2];10]}

การใช้

q){0=mod[(+/)"I"$(,/)($)($)@["I"$'x;1+2*'(!)(_)((#)x)%2;*;2];10]}"79927398711"
0b
q){0=mod[(+/)"I"$(,/)($)($)@["I"$'x;1+2*'(!)(_)((#)x)%2;*;2];10]}"79927398712"
0b
q){0=mod[(+/)"I"$(,/)($)($)@["I"$'x;1+2*'(!)(_)((#)x)%2;*;2];10]}"79927398713"
1b

47 ไบต์ด้วย{0=mod[sum"J"$raze($)($)x*#:[x]#1 2]10}"I"$'(|)วิธีที่แตกต่างกันในการเพิ่มดัชนีคี่สองเท่า
streetster

5

D, 144 ไบต์

bool f(S)(S s){int t(C)(C c){return to!int(c)-'0';}int n,v;foreach(i,c;array(retro(s))){v=i&1?t(c)*2:t(c);n+=v>=10?v%10+v/10:v;}return n%10==0;}

ชัดเจนมากขึ้น:

bool f(S)(S s)
{
    int t(C)(C c)
    {
        return to!int(c) - '0';
    }

    int n, v;

    foreach(i, c; array(retro(s)))
    {
        v = i & 1 ? t(c) * 2 : t(c);

        n += v >= 10 ? v % 10 + v / 10 : v;
    }

    return n % 10 == 0;
}

5

APL, 28 ไบต์

{0=10|+/⍎¨∊⍕¨v×⌽2-2|⍳⍴v←⍎¨⍵}

มุมมองการระเบิด

{                     v←⍎¨⍵}  ⍝ turn the string into a numeric vector of its digits, v
                2-2|⍳⍴v       ⍝ make a vector of the same length, with 2 in every 2nd place
             v×⌽              ⍝ multiply it with v, starting from the right
          ∊⍕¨                 ⍝ turn each component into a string and collect all the digits
      +/⍎¨                    ⍝ turn each digit again into a number and sum them
 0=10|                        ⍝ check whether the sum is a multiple of 10

ตัวอย่าง

      {0=10|+/⍎¨∊⍕¨v×⌽2-2|⍳⍴v←⍎¨⍵} '79927398713'
1
      {0=10|+/⍎¨∊⍕¨v×⌽2-2|⍳⍴v←⍎¨⍵} '123456789'
0

1
-2:{0=10|+/⍎¨∊⍕¨⍵×⌽2-2|⍳⍴⍵}⍎¨
2560


4

Perl, 46 42 41 ไบต์

รวม +1 สำหรับ -p

ให้อินพุตบน STDIN:

luhn.pl <<< 79927398713

luhn.pl:

#!/usr/bin/perl -p
s%.%$=-=-$&-$&*1.2*/\G(..)+$/%eg;$_=/0$/

คุณช่วยอธิบายวิธีการทำงานของมันได้มั้ย คุณดูเหมือนจะลดลงจากการแข่งขันและจากนั้นโดยการแข่งขันครั้งที่ 1.2 แต่เฉพาะในตำแหน่งที่ถูกต้อง ทำไมต้อง 1.2? ไม่ควร$=-=-$&-$&*/\G(..)+$/หรือ
msh210

3
@ msh210: มันเข้ารหัสผลกระทบของการคูณด้วย 2 0..4* 2 ให้0, 2, 4, 6, 8แต่5..9ให้10,12,14,16,18ผลรวม1 3 5 7 9ที่มีตัวเลขสุดท้ายเหมือนกัน11 13 15 17 19ซึ่งเป็นค่าเดียวกับ0..9 * 2.2ถ้าคุณตัดให้เป็นจำนวนเต็ม ครั้งแรกที่$&จัดสรรปัจจัย1ดังนั้นการแก้ไขโดย1.2ยังจำเป็น $=สามารถเก็บจำนวนเต็มและเริ่มต้นด้วยค่าที่ลงท้ายด้วย 0 ดังนั้นดูแลการตัดทอน ค่าลบมีความจำเป็นเนื่องจาก/\G/regex เปลี่ยนแปลง$&ยังคงอยู่ในกองการประเมินผลเพื่อให้พวกเขาจำเป็นต้องเปลี่ยน
Ton Hospel

โอ้ ยอดเยี่ยม! และขอบคุณสำหรับคำอธิบาย
msh210

3

JavaScript (ES6), 61 ไบต์

ไม่แข่งขันเนื่องจาก JavaScript แตกต่างกันมากในปี 2011

ผลรวมของตัวเลขของ2*nคือ2*nถ้าn in 0..4, ถ้า2*n-9 n in 5..9ที่กล่าวว่าผลรวมทั้งหมดสามารถคำนวณได้ในขั้นตอนเดียว

s=>!([...s].reduceRight((t,d)=>t-d-i++%2*(d>4?d-9:d),i=0)%10)

3

เจลลี่ , 12 11 ไบต์

ṚḤJḤ$¦DFS⁵ḍ

ลองออนไลน์! (กับกรณีทดสอบทั้งหมด)

มันทำงานอย่างไร

ṚḤJḤ$¦DFSḍ⁵  - Main link. Argument: n (integer) e.g. 49927398716
Ṛ            - Reverse. Casts a number to digits     [6, 1, 7, 8, 9, 3, 7, 2, 9, 9, 4]
     ¦       - Sparse application. Apply the next command to the given indicies
 Ḥ           -   Command: Double
    $        -   Indicies:
  J          -     range(length)...                  [1, 2 , 3, 4, 5, 6, 7, 8, 9, 10, 11]
   Ḥ         -     doubled.                          [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22]
             - Doubles elements at odd indicies      [6, 2, 7, 16, 9, 6, 7, 4, 9, 18, 4]
      D      - Split each into digits                [6, 2, 7, [1, 6], 9, 6, 7, 4, 9, [1, 8], 4]
       F     - Flatten                               [6, 2, 7, 1, 6, 9, 6, 7, 4, 9, 1, 8, 4]
        S    - Sum                                   70
          ḍ  - Divisible by... 
         ⁵   -   10?                                 1

อีกทางเลือกหนึ่งสำหรับ 12 ไบต์:

ṚḤJḤ$¦DFSḍ@⁵

3

8086 Assembly , IBM PC DOS,29 28 25 23 ไบต์

; Perform LUHN check
; Input: SI = card num string, CX = length
; Output: ZF = 1 if valid, ZF = 0 if not valid
    LUHN    MACRO
            LOCAL DIGIT_LOOP, EVEN
03 F1       ADD  SI, CX         ; start at end of input string 
FD          STD                 ; set LODSB direction to decrement 
    DIGIT_LOOP:
AC          LODSB               ; load next digit into AL, decrement SI
2C 30       SUB  AL, '0'        ; convert ASCII char to binary value 
F7 DA       NEG  DX             ; flip DX to alternate odd/even index
78 06       JS   EVEN           ; if even index, do not double and sum digits 
D0 E0       SHL  AL, 1          ; double the value 
D4 0A       AAM                 ; BCD convert to split digits (ex: 18 = 12H --> 0108H) 
02 DC       ADD  BL, AH         ; add tens digit to running sum 
    EVEN:
02 D8       ADD  BL, AL         ; add ones digit to running sum 
E2 ED       LOOP DIGIT_LOOP 
93          XCHG BX, AX         ; sum is in BL, move to AL for conversion
D4 0A       AAM                 ; BCD convert AL, set ZF=1 if low digit is 0
    ENDM

ใช้ (ละเมิด) คำแนะนำ BCD-to-binary ของ x86 เพื่อจัดการการแยกและmodulo 10ตรวจสอบตัวเลขแต่ละหลัก ปรากฎว่าAAMและAADคำแนะนำทำการแปลง BCD / ไบนารีของค่าไบต์ (ซึ่งมีประโยชน์มาก) แม้ว่าพวกเขาจะไม่ได้รับการบันทึกหรืออธิบายว่าเป็นการทำงานของฟังก์ชั่นทั่วไป

อย่างเป็นทางการทุกรุ่น IBM / MS DOS, AXและBXจะเริ่มต้นได้0000ในการเริ่มต้น ( เตะ , เตะโทษ ) และDXไปCSซึ่งเป็นค่า 12 บิตรับประกันมากที่จะไม่เป็นศูนย์และบวก นี่คือวิธีที่เรารับประกันBXได้0และสามารถพลิก / ฟลอปDXเพื่อระบุสถานที่แปลก ๆ / เลขคู่

ตัวอย่างผลลัพธ์:

ป้อนคำอธิบายรูปภาพที่นี่

ดาวน์โหลดโปรแกรมทดสอบLUHN.COM IBM PC DOS


2

สกาล่า: 132

def q(x:Int)=x%10+x/10
def c(i:String)={val s=i.reverse
(s(0)-48)==10-(s.tail.sliding(2,2).map(n=>(q((n(0)-48)*2)+n(1)-48)).sum%10)}

ภาวนา:

c("79927398713")
  • ย้อนกลับ ("79927398713") = 31789372997
  • s (0), s.tail: (3) (1789372997)
  • เลื่อน (2,2) = (17 89 37 29 97)
  • map (q ((n (0) -48 * 2 + n (1) -48)) => q (('1' - '0') * 2) + '7' - '0') = 1 * 2 + 7

2

JavaScript 1.8: 106 ตัวอักษร

นี่เป็นวิธีแก้ปัญหาดั้งเดิมที่ฉันพบก่อนที่ฉันจะพบโพสต์นี้:

function(n){return!(n.split('').reverse().reduce(function(p,c,i){return(+c&&((c*(1+i%2)%9)||9))+p},0)%10)}

แบบฟอร์มที่อ่านได้:

function luhnCheck(ccNum) {
    return !(                                  // True if the result is zero.
             ccNum.split('').
               reverse().                      // Iterate over the string from rtl.
               reduce(function(prev, cur, idx) {
                 return prev +                 // Sum the results of each character.
                        (+cur &&               // If the current digit is 0, move on.
                         ((cur * (1 + idx % 2) // Double cur at even indices.
                           % 9) || 9));        // Sum the digits of the result.
               }, 0)
            % 10);                             // Is the sum evenly divisible by 10?
}


2

เรติน่า43 43ไบต์

จอประสาทตานั้นใหม่กว่าความท้าทายนี้มาก


;
r`(.);.
$1$&
\d
$*
1+
$.&
.
$*
$
$._
0$

บรรทัดว่างนำหน้ามีความสำคัญ

พิมพ์0เพื่อความเท็จและ1เพื่อผลลัพธ์ที่แท้จริง

ลองออนไลน์! (แก้ไขเล็กน้อยเพื่อเรียกใช้กรณีทดสอบทั้งหมดในครั้งเดียว)

คำอธิบาย


;

แทรก;ทุกตำแหน่งเพื่อแยกตัวเลข

r`(.);.
$1$&

จากright เราจับคู่ตัวเลขสองหลักซ้ำแล้วซ้ำกันเป็นสองเท่า วิธีนี้เราหลีกเลี่ยงการย้อนกลับของรายการที่มีราคาแพง

\d
$*

เราจับคู่แต่ละหลักแล้วแปลงเป็นจำนวนมาก1(นั่นคือเราแปลงแต่ละหลักเป็นเอก)

1+
$.&

สิ่งนี้จะจับคู่หมายเลขเอกนารีแต่ละตัวและแปลงกลับเป็นทศนิยมโดยแทนที่ด้วยความยาว เมื่อรวมกับระยะก่อนหน้านี้จะเพิ่มตัวเลขสองหลัก

.
$*

อีกครั้งเราจับคู่ตัวละครทุกตัวแล้วเปลี่ยนเป็นตัวละครหลาย1ตัว นั่นคือเราแปลงตัวเลขแต่ละหลักกลับเป็นเอกภาพ นอกจากนี้ยังตรงกับ;ตัวคั่นซึ่งถือว่าเป็นศูนย์ในการแปลงซึ่งหมายความว่าพวกมันถูกลบออก เนื่องจากหมายเลขยูนารีทั้งหมดถูกบีบเข้าด้วยกันเราจึงได้เพิ่มการแสดงตัวเลขทั้งหมดเข้าด้วยกันโดยอัตโนมัติ

$
$._

ในตอนท้ายเราใส่ความยาวของสตริงทั้งหมดเช่นการแทนทศนิยมของการตรวจสอบ unary

0$

ในที่สุดเราก็นับจำนวนการแข่งขันของ regex นี้คือเราตรวจสอบว่าการแสดงทศนิยมสิ้นสุดลงใน0การพิมพ์0หรือ1ตาม


2

Powershell, 74 ไบต์

param($s)$s[$s.Length..0]|%{(1+$i++%2)*"$_"}|%{$r+=$_-9*($_-gt9)}
!($r%10)

คำอธิบาย

  1. สำหรับอักขระแต่ละตัวของสตริงอาร์กิวเมนต์ในลำดับที่กลับกัน
  2. รับตัวเลขสองหลักของค่า
  3. ค่าสองหลักของตัวเลขไม่สามารถมากกว่า 18 ดังนั้นเราจึงสะสมค่าลบ 9 หากค่า> 9
  4. ผลตอบแทนจริงถ้าส่วนที่เหลือของการหารด้วย 10 คือ 0

สคริปต์ทดสอบ

$f = {

param($s)$s[$s.Length..0]|%{(1+$i++%2)*"$_"}|%{$r+=$_-9*($_-gt9)}
!($r%10)

}

@(
    ,("49927398716"      , $True)
    ,("49927398717"      , $False)
    ,("1234567812345670" , $True)
    ,("1234567812345678" , $False)
    ,("79927398710"      , $False)
    ,("79927398711"      , $False)
    ,("79927398712"      , $False)
    ,("79927398713"      , $True)
    ,("79927398714"      , $False)
    ,("79927398715"      , $False)
    ,("79927398716"      , $False)
    ,("79927398717"      , $False)
    ,("79927398718"      , $False)
    ,("79927398719"      , $False)
    ,("374652346956782346957823694857692364857368475368" , $True)
    ,("374652346956782346957823694857692364857387456834" , $False)
    ,("8" , $False)
    ,("0" , $True)
) | % {
    $s, $expected = $_
    $result = &$f $s
    "$($result-eq$expected): $result : $s"
}

เอาท์พุต

True: True : 49927398716
True: False : 49927398717
True: True : 1234567812345670
True: False : 1234567812345678
True: False : 79927398710
True: False : 79927398711
True: False : 79927398712
True: True : 79927398713
True: False : 79927398714
True: False : 79927398715
True: False : 79927398716
True: False : 79927398717
True: False : 79927398718
True: False : 79927398719
True: True : 374652346956782346957823694857692364857368475368
True: False : 374652346956782346957823694857692364857387456834
True: False : 8
True: True : 0



1

GNU sed, 140 ไบต์

(รวมถึง +1 สำหรับ-rธง)

s/^(..)*.$/0&/
s/(.)./\1x&/g
s/x[5-9]/1&/g
s/[0x]//g
s/[789]/&6/g
s/[456]/&3/g
s/[369]/&11/g
s/[258]/&1/g
s/.{10}//g
s/.+/false/
s/^$/true/

Sed ไม่ได้เป็นภาษาธรรมชาติที่สุดสำหรับคณิตศาสตร์ แต่ที่นี่เราไป:

#!/bin/sed -rf

# zero-pad to even length
s/^(..)*.$/0&/
# double every other digit
s/(.)./\1x&/g
# add carry (converts mod-9 to mod-10)
s/x[5-9]/1&/g
# convert sum to unary
s/[0x]//g
s/[789]/&6/g
s/[456]/&3/g
s/[369]/&11/g
s/[258]/&1/g
# remove whole tens
s/.{10}//g
# output 'true' or false
s/.+/false/
s/^$/true/

1

APL, 38 ไบต์

d←10∘⊥⍣¯1⋄{0=10|+/+/d x×1+~2|⍳⍴x←⌽d ⍵}

คาดหวังว่าตัวเลขเป็นตัวเลขไม่ใช่สตริง แต่เป็นเพราะtryAPL (เข้าใจแล้ว) ไม่ได้ใช้

ลดได้อีกแน่นอนฉันแน่ใจว่า ...



1

MATL , 23 20 ไบต์ (ไม่ใช่การแข่งขัน)

P!Utn:2X\!*t9>+s10\~

ลองออนไลน์!

เอาต์พุต 1 สำหรับหมายเลขที่ถูกต้องมิฉะนั้นเป็น 0

บันทึกสามไบต์ด้วยคำแนะนำของ Luis Mendo

คำอธิบาย

P       % flip the order of elements
!       % transpose into column vector
U       % convert char matrix to numeric
t       % duplicate the vector
n       % find the length
:       % create a new vector length n (1, 2, 3, ... n)
2       % number literal
X\      % take it mod 2, to make the new vector (1, 2, 1, ..., (n-1) mod 2 +1)
!       % transpose
*       % element-wise product
t       % duplicate
9       % push 9
>       % 1 if it is greater than 9
+       % add the vectors, this makes the last digit of each the same as the sum of the digits
s       % add them
10      % number literal
\       % mod 10
~       % logical 'not' (element-wise)
        % (implicit) convert to string and display

1

เยลลี่ขนาด 14 ไบต์

DUḤJḤ$¦DS$€S⁵ḍ

ลองออนไลน์!

คำอธิบาย:

D              get digits
 U             reverse array
   JḤ$         for every other index,
  Ḥ   ¦        double the value
          €    for each value,
       D $     get the digits
        S$     and sum them
           S   sum the list
            ⁵ḍ check if it's divisible by 10

ทำไมสิ่งนี้จึงไม่ใช่การแข่งขัน
mudkip201

@ mudkip201 แก้ไขให้ถูกต้องหากฉันผิด แต่ Jelly รุ่นนี้ไม่มีอยู่เมื่อมีการถามคำถามดังนั้นจึงไม่ถูกต้องสำหรับคำถาม
Ellie

3
ค่อนข้างแน่ใจว่ามีฉันทามติ meta ที่กล่าวว่าภาษาที่สร้างขึ้นหลังจากความท้าทายไม่ใช่ 'ไม่แข่งขัน' อีกต่อไป
mudkip201
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.