แผ่นป้ายทะเบียนที่สมบูรณ์แบบ


33

แผ่นป้ายทะเบียนที่สมบูรณ์แบบ

เมื่อไม่กี่ปีที่ผ่านมาฉันทำให้ตัวเองเป็นเกมเล็ก ๆ ในขณะขับรถไปรอบ ๆ : ตรวจสอบว่าแผ่นป้ายทะเบียนใกล้เคียงนั้น“ สมบูรณ์แบบ” หรือไม่ มันค่อนข้างหายาก แต่น่าตื่นเต้นเมื่อคุณพบมัน

วิธีตรวจสอบว่าแผ่นป้ายทะเบียนนั้นสมบูรณ์หรือไม่:

  1. สรุปอักขระด้วย A = 1, B = 2, ... Z = 26
  2. ใช้ตัวเลขที่ต่อเนื่องกันและรวมเข้าด้วยกัน คูณผลรวมเหล่านี้เข้าด้วยกัน

หากค่าในส่วนที่ 1 และส่วนที่ 2 เท่ากันขอแสดงความยินดีด้วย! คุณพบป้ายทะเบียนรถที่สมบูรณ์แบบแล้ว!

ตัวอย่าง

License plate: AB3C4F

Digits -> 3 * 4 
        = 12
Chars  -> A + B + C + F 
        = 1 + 2 + 3 + 6 
        = 12
12 == 12 -> perfect!


License plate: G34Z7T

Digits -> (3 + 4) * 7 
        = 49
Chars  -> G + Z + T 
        = 7 + 26 + 20 
        = 53
49 != 53 -> not perfect!


License plate: 10G61

Digits -> (1 + 0) * (6 + 1)
        = 7
Chars  -> G
        = 7
7 == 7 -> perfect!

ความท้าทาย

ฉันใช้แผ่นป้ายทะเบียนความยาว 5 และ 6 เป็นตัวอย่าง แต่ขั้นตอนนี้ใช้ได้กับความยาวแผ่นใด ๆ ความท้าทายของคุณคือสำหรับความยาวที่กำหนด N ให้คืนค่าจำนวนแผ่นป้ายทะเบียนที่สมบูรณ์แบบของความยาวนั้น สำหรับวัตถุประสงค์ของการท้าทายป้ายทะเบียนที่ถูกต้องคือการรวมกันของตัวเลข 0-9 และตัวอักษร AZ จานจะต้องมีทั้งตัวละครและตัวเลขที่จะถือว่าสมบูรณ์แบบ เพื่อตรวจสอบจุดประสงค์นี่คือค่าที่ฉันได้รับ (แม้ว่าฉันจะไม่ถูกต้อง 100% เกี่ยวกับความถูกต้องของพวกเขาฮ่าฮ่าฮ่า)

N < 2: 0
N = 2: 18
N = 3: 355
N = 4: 8012 

หมายเหตุ

ถ้าอย่างใดมันทำให้ปัญหาง่ายขึ้นในภาษาของคุณคุณอาจส่งออกสัดส่วนของแผ่นป้ายทะเบียนที่สมบูรณ์แบบสำหรับ N ที่กำหนดให้กับตัวเลขที่มีนัยสำคัญอย่างน้อย 2 หลัก

N < 2: 0
N = 2: 0.0138888...
N = 3: 0.0076088...
N = 4: 0.0047701...

หรือคุณอาจส่งออกค่าเทียบเท่าmod 256

N < 2: 0
N = 2: 18
N = 3: 99
N = 4: 76

ชนะสั้นที่สุด!


2
ยินดีต้อนรับสู่เว็บไซต์! ฉันคิดว่านี่เป็นความท้าทายที่ดี แต่ผลลัพธ์ที่ได้รับอนุญาตเพิ่มเติมทำให้ยากที่จะให้คะแนนคำตอบจริง ๆ PPCG ค้นหาเกณฑ์การชนะอย่างมีเป้าหมายและมันยากที่จะทำเช่นนั้นเมื่อมีเสรีภาพมากมาย สิ่งนี้ไม่เพียงเปลี่ยนรูปแบบผลลัพธ์ แต่สิ่งนี้จะเปลี่ยนสิ่งที่คุณได้รับอนุญาต ฉันจะแนะนำการแก้ไขจากตัวเลือกอื่น ๆ Nและเพียงแค่ทำให้มันจำเป็นต้องมีการส่งออกจำนวนแผ่นป้ายทะเบียนที่สมบูรณ์แบบสำหรับ
HyperNeutrino

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

4
ฉันเห็นด้วยกับ Mistah Figgins; ฉันรู้สึกว่าวิธีนี้มันเกี่ยวกับการหารูปแบบซึ่งยังคงเป็นความท้าทายที่น่าสนใจ แต่ฉันคิดว่ามันอาจดึงดูดคำตอบได้มากขึ้นหากเป็นการตรวจสอบความถูกต้อง แม้ว่าจะท้าทายดี!
HyperNeutrino

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

1
@carusocomputing ฉันพยายามอย่างดีที่สุด แต่ก็ว่างเปล่า ฉันส่งมันให้ครูคณิตศาสตร์ของฉันและเขาว่างเปล่าแล้ว
Christopher

คำตอบ:


9

Python 3.6, 150 ไบต์

f=lambda n,t=-1,p=-1,a=0:sum(f(n-1,*((t,c+p*(p>=0),a),((t<0 or t)*(p<0 or p),-1,a-c))[c<0])for c in range(-26,10))if n else 0<(t<0 or t)*(p<0 or p)==a

ผล:

f(2) = 18
f(3) = 355
f(4) = 8012
f(5) = 218153

รุ่นที่ไม่ได้รับการอธิบายพร้อมคำอธิบาย:

digits=[*range(10)]
letters=[*range(1,27)]

def f(n, dt=-1, dp=-1, lt=0):
    if n:
        for d in digits:
            yield from f(n - 1,
                         dt,
                         d if dp < 0 else dp + d,
                         lt
                         )

        for l in letters:
            yield from f(n - 1,
                         dp if dt < 0 else dt if dp < 0 else dt*dp,
                         -1,
                         lt + l
                         )
    else:
        yield 0 < lt == (dt<0 or dt)*(dp<0 or dp)

ปัญหาเดือดลงมาเพื่อค้นหาต้นไม้ที่แต่ละระดับของต้นไม้สอดคล้องกับตำแหน่งในหมายเลขทะเบียนรถยนต์และแต่ละโหนดมีลูก 36 คน (10 หลักและ 26 ตัวอักษร) ฟังก์ชั่นทำการค้นหาแบบทวนซ้ำของทรีสะสมค่าสำหรับตัวเลขและตัวอักษรตามที่ไป

n is the number of levels to search. 
dp accumulates the sum of a group of digits.
dt accumulates the product of the digit sums.
lt accumulates the sum of the letter values.

For dp and dt, a value < 0 indicates it is not initialized.

กอล์ฟรวมถึงการแปลงลูปเป็นผลรวมของเครื่องกำเนิดไฟฟ้า:

sum(f(n-1, 
      dt,
      d if dp < 0 else dp + d,
      lt) for d in digits)
+
sum(f(n-1,
      dp if dt<0 else dt if dp<0 else dt*dp,
      -1,
      lt+l) for l in letters)

จากนั้นรวมเครื่องกำเนิดไฟฟ้า เข้ารหัสตัวอักษร A ถึง Z เป็น -1 ถึง -26 และตัวเลขเป็น 0 ถึง 9 ดังนั้นผลรวมจะกลายเป็น:

sum(f(n-1, *args) for c in range(-26, 10)),

โดยที่ args คือ:

((dp if dt<0 else dt if dp<0 else dt*dp, -1, lt-l) if c <0 else
 (dt, d if dp<0 else dp+d, lt))

ส่วนที่เหลือของการเล่นกอล์ฟคือการแปลงฟังก์ชั่นเป็นแลมบ์ดาทำให้ชื่อตัวแปรสั้นลงและทำให้การแสดงออกง่ายขึ้น


นี่เป็นวิธีแก้ปัญหาฝีปากรันไทม์จะเป็นอย่างไร n*n*log(n)หรือสิ่งที่คล้ายกัน
Magic Octopus Urn

@carusocomputing ขอบคุณ โซลูชันยังคงสร้างการเรียงสับเปลี่ยนที่เป็นไปได้ทั้งหมดตามความยาวที่กำหนดดังนั้นจึงมีความซับซ้อนเช่นเดียวกับโซลูชันอื่น บางอย่างเช่น k ** n โดยที่ k คือจำนวนสัญลักษณ์ในตัวอักษร (เช่น 10 หลัก + 26 ตัวอักษร = 36) และ n คือจำนวนของสัญลักษณ์บนแผ่นป้ายทะเบียน การเรียกใช้สำหรับ n = 5 ต้องตรวจสอบ 36 ^ 5 = 60,466,176 การเรียงสับเปลี่ยนและใช้เวลาหนึ่งหรือสองนาที (การบันทึกอาจเพิ่มความเร็ว
RootTwo

6

Dyalog APL, 57 56 ไบต์

+/(+/0⌈a-9)=×/c*⍨-2-/0,⌈\(+\a×b)×c←2>/0,⍨b←9≥a←↑1↓,⍳⎕⍴36

(ถือว่า⎕io←0)

aเมทริกซ์ของแผ่นป้ายทะเบียนที่ถูกต้องทั้งหมด (ยกเว้น00...0) เข้ารหัสด้วย: 0-9 สำหรับหลัก, 10-35 สำหรับตัวอักษร

b bitmask สำหรับที่เกิดตัวเลข

c bitmask สำหรับตัวเลขสุดท้ายในทุกกลุ่มของตัวเลขที่ต่อเนื่องกัน


ลองใช้งานออนไลน์สำหรับ 1-4 ต้องการหน่วยความจำเพิ่มสำหรับ 4 แต่ก็มีหลายวิธีด้วย
อดัม

4

Python 2, 359 295 ไบต์

ค่อนข้างยาว นี่เป็นวิธีแก้ปัญหาเล็กน้อย ฉันมั่นใจว่าถูกต้องแม้ว่าจะไม่ตรงกับกรณีทดสอบในการทดสอบ คำตอบที่ฉันได้รับตรงกับคำตอบของ Dada

import itertools as i,re as r,string as s
print len([''.join(x)for x in i.product(s.lowercase+s.digits,repeat=input())if(lambda t:r.search('\\D',t)and r.search('\\d',t)and reduce(int.__mul__,[sum(map(int,k))for k in r.split('\\D+',t)if k])==sum([k-96 for k in map(ord,t) if k>96]))(''.join(x))])

-64 ไบต์ขอบคุณคำแนะนำจาก @numbermaniac


1
คุณสามารถบันทึกประมาณสามไบต์ใน c (x) และบรรทัดสุดท้าย; ลบช่องว่างระหว่าง 96 และfor; ระหว่างmap(ord,x)และif; และในบรรทัดสุดท้ายระหว่างและ.join(x) forฉันคิดว่าคุณสามารถประหยัดได้มากขึ้นหากคุณกำหนดฟังก์ชั่นใหม่เป็น lambdas
numbermaniac

@numbermaniac ขอบคุณ! (ทั้งหมด 64 ไบต์)
HyperNeutrino

4

Python 2 , 291 287 276 273 ไบต์

lambda n:sum(1for x in s.product(y+z,repeat=n)if(lambda p,s=set:reduce(int.__mul__,[sum(map(int,i))for i in re.findall(r"\d+",p)],1)==sum(ord(i)-64for i in p if ord(i)>64)and s(p)&s(y)and s(p)&s(z))(''.join(x)))
import re,itertools as s,string as t
y=t.uppercase
z=t.digits

ลองออนไลน์!


ผล:

0 0
1 0
2 18
3 355
4 8012

3

Perl 5 , 117 ไบต์

116 ไบต์ของรหัสเมือง + -pธง

$"=",";@F=(A..Z,0..9);map{$r=1;$r*=eval s/./+$&/gr for/\d+/g;$r+=64-ord for/\pl/g;$\+=!$r*/\pl/*/\d/}glob"{@F}"x$_}{

ลองออนไลน์!

รู้สึกค่อนข้างไม่ดีนัก แต่ตอนนี้ฉันหมดความคิดแล้ว
รหัสตัวเองไม่มีประสิทธิภาพมากเพราะมันคำนวณการเปลี่ยนแปลงa..z,0..9ความยาวทุกครั้งn(ใช้เวลาประมาณ 1 วินาทีn=3ประมาณ ~ 15 วินาทีn=4และ ~ 7 นาทีn=5)
อัลกอริธึมนั้นค่อนข้างตรงไปตรงมา: สำหรับทุกขนาดของแผ่นที่เป็นไปได้n(สร้างด้วยglob"{@F}"x$_- ตัวglobดำเนินการนั้นค่อนข้างน่าอัศจรรย์) $r*=eval s/./+$&/gr for/\d+/g;คำนวณผลคูณของตัวเลขทุกอันและ$r+=64-ord for/\pl/gลบน้ำหนักของตัวอักษร จากนั้นเราจะเพิ่มตัวนับ$\หาก$rเป็น0( !$r) และหากแผ่นมีตัวเลขและตัวอักษร ( /\pl/*/\d/) $\ถูกพิมพ์โดยปริยายในตอนท้ายด้วยการ-pตั้งค่าสถานะ

โปรดทราบว่าตัวเลขที่ฉันได้รับมีn=2 -> 18, n=3 -> 355, ,n=4 -> 8012 n=5 -> 218153ฉันค่อนข้างแน่ใจว่าสิ่งเหล่านี้เป็นสิ่งที่ถูกต้อง แต่ฉันอาจเข้าใจผิดซึ่งในกรณีนี้ให้ฉันรู้และฉันจะลบคำตอบนี้


3

APL (Dyalog) 71 ไบต์

โปรแกรมร่างกายเต็ม พรอมต์สำหรับ N. N≥4ต้องการหน่วยความจำและการคำนวณจำนวนมาก

+/((+/⊢⍳∩)∘⎕A=(×/'\d+'S{+/⍎¨⍵.Match}))¨l/⍨∧⌿∨/¨c∘.∊l←,(∊c←⎕DA)∘.,⍣⎕⊂⍬

ลองออนไลน์!


2

สกาลา 265 ไบต์

(n:Int)=>{val i=('A'to'Z')++('0'to'9');Seq.fill(n)(i).flatten.combinations(n).flatMap(_.permutations).map(_.mkString).count(l=>"(?=.*[A-Z])(?=.*\\d)".r.findAllIn(l).size>0&&l.map(_-64).filter(_>0).sum==l.split("[A-Z]").filter(""<).map(_.map(_-48).sum).reduce(_*_))}

คำอธิบาย:

(n:Int) => {
    val i = ('A' to 'Z') ++ ('0' to '9');                       // All license plates available characters.
    Seq.fill(n)(i).flatten                                      // Simulate combination with repetition (each character is present n times)
        .combinations(n)                                        // and generate all combinations of size n (all license plates).
        .flatMap(_.permutations)                                // For each combination, generate all permutations (ex. : Seq('A', '1') => Seq('A', '1') and Seq('1', 'A')), and
        .map(_.mkString)                                        // convert each permutation to String (Seq('A', '1') => "A1").
        .count ( l =>                                           // Then count all strings having :
            "(?=.*[A-Z])(?=.*\\d)".r.findAllIn(l).size > 0 &&   // at least 1 character and 1 digit and
            l.map(_ - 64).filter(_ > 0).sum ==                  // a sum of characters (> 'A' or > 64) equals to
            l.split("[A-Z]").filter(""<)
                .map(_.map(_ - 48).sum)
                .reduce(_*_)                                    // the product of sum of digits groups (split String by letters to get digits groups)
        )
}

หมายเหตุ:

  • -64และ-48ถูกนำมาใช้ในการแปลงChar(ตามลำดับตัวอักษรและหลัก) เพื่อของIntค่า ( 'A' - 64 = 1, 'B' - 64 = 2, ... '9' - 48 = 9)
  • ในตัวกรองl.split("[A-Z]").filter(""<)ที่ใช้ในการกำจัด""ค่าถ้าlเริ่มต้นด้วยตัวอักษร (ตัวอย่าง: "A1".split("[A-Z]") => Array("", 1)) อาจมีทางออกที่ดีและสั้นกว่า

กรณีทดสอบ:

val f = (n:Int) => ...  // assign function
(1 to 5).foreach ( i =>
    println(s"N = $i: ${f(i)}")
)

ผล :

N = 1: 0
N = 2: 18
N = 3: 355
N = 4: 8012
N = 5: 218153

ฟังก์ชั่นค่อนข้างช้าn > 4เนื่องจากต้องสร้างชุดค่าผสมทั้งหมด


2

Java, 382 365 ไบต์

  • บันทึกแล้ว 17 ไบต์ขอบคุณKevin Cruijssen

แข็งแรงเล่นกอล์ฟ

int h(String s){int m=0;for(int c:s.toCharArray())m+=c-48;return m;}
int g(String t){int d=1,c=0;for(String s:t.split("[^0-9]"))d*=h(s);for(String s:t.split("[^A-Z]"))c+=s.charAt(0)-65;return d==c?1:0;}
int f(String t,int n){int m=0;if(t.length()==n)return g(t);for(int d=48;d<58;)m+=f(t+d++,n);for(int c=65;c<91;)m+=f(t+c++,n);return m;}
int s(int n){return f("",n);}

รายละเอียด

// return sum of adjecent digits
int h(String s)
{
    int sum = 0;
    for(char c : s.toCharArray()) sum += c-'0';
    return sum;
}

// check if perfect
int g(String t)
{
    int d = 1;
    int c = 0;

    for(String s : t.split("[^0-9]")) d *= h(s);
    for(String s : t.split("[^A-Z]")) c += s.charAt(0)-'A';

    return d == c ? 1 : 0;
}

// tree of enumerations
int f(String t, int n)
{
    // base case
    if(t.length() == n)
    {
        return g(t);
    }

    // enumeration
    int sum = 0;
    for(char d='0'; d<='9'; d++) sum += f(t+d, n);
    for(char c='A'; c<='Z'; c++) sum += f(t+c, n);

    return sum;
}

int s(int n){ return f("",n); }

ฉันคิดว่าคุณต้องการฟังก์ชั่นที่ใช้nเป็นอินพุตเท่านั้น
Christian Sievers


1
สิ่งที่ต้องตีกอล์ฟสำหรับรหัสปัจจุบันของคุณ: int h(String s){int m=0;for(int c:s.toCharArray())m+=c-48;return m;}int g(String t){int d=1,c=0;for(String s:t.split("[^0-9]"))d*=h(s);for(String s:t.split("[^A-Z]"))c+=s.charAt(0)-65;return d==c?1:0;}int f(String t,int n){int m=0;if(t.length()==n)return g(t);for(int d=48;d<58;)m+=f(t+d++,n);for(int c=65;c<91;)m+=f(t+c++,n);return m;}int s(int n){return f("",n);}( 365 ไบต์ ) คุณสามารถเปรียบเทียบเวอร์ชันปัจจุบันของคุณกับสิ่งนี้เพื่อดูการเปลี่ยนแปลงที่ฉันทำ (มากเกินไปที่จะพอดีกับส่วนที่เหลือของความคิดเห็นนี้) :)
Kevin Cruijssen

@KevinCruijssen ขอบคุณ, 17 ไบต์ปิดตอนนี้
Khaled.K

2

GAP , 416 ไบต์

จะไม่ชนะขนาดรหัสและห่างจากเวลาคงที่ แต่ใช้คณิตศาสตร์เพื่อเร่งความเร็วให้มากขึ้น!

x:=X(Integers);
z:=CoefficientsOfUnivariatePolynomial;
s:=Size;

f:=function(n)
 local r,c,p,d,l,u,t;
 t:=0;
 for r in [1..Int((n+1)/2)] do
  for c in [r..n-r+1] do
   l:=z(Sum([1..26],i->x^i)^(n-c));
   for p in Partitions(c,r) do
    d:=x;
    for u in List(p,k->z(Sum([0..9],i->x^i)^k)) do
     d:=Sum([2..s(u)],i->u[i]*Value(d,x^(i-1))mod x^s(l));
    od;
    d:=z(d);
    t:=t+Binomial(n-c+1,r)*NrArrangements(p,r)*
         Sum([2..s(d)],i->d[i]*l[i]);
   od;
  od;
 od;
 return t;
end;

หากต้องการบีบพื้นที่ว่างที่ไม่จำเป็นออกมาและรับหนึ่งบรรทัดด้วย 416 ไบต์ไปที่:

sed -e 's/^ *//' -e 's/in \[/in[/' -e 's/ do/do /' | tr -d \\n

แล็ปท็อป "รุ่นเก่าที่ออกแบบมาสำหรับ Windows XP" ของฉันสามารถคำนวณได้f(10)ในเวลาไม่ถึงหนึ่งนาที

gap> for i in [2..15] do Print(i,": ",f(i),"\n");od;
2: 18
3: 355
4: 8012
5: 218153
6: 6580075
7: 203255386
8: 6264526999
9: 194290723825
10: 6116413503390
11: 194934846864269
12: 6243848646446924
13: 199935073535438637
14: 6388304296115023687
15: 203727592114009839797

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

สมมติว่าในตอนแรกเราแค่ต้องการทราบจำนวนแผ่นป้ายทะเบียนที่สมบูรณ์แบบที่เหมาะสมกับรูปแบบLDDLLDLซึ่งLหมายถึงตัวอักษรและ Dหมายถึงตัวเลข สมมติว่าเรามีรายการlตัวเลขที่ l[i]ให้จำนวนวิธีที่ตัวอักษรสามารถให้ค่าiและรายการที่คล้ายกันdสำหรับค่าที่เราได้รับจากตัวเลข แล้วจำนวนแผ่นป้ายทะเบียนที่สมบูรณ์แบบที่มีค่าร่วมกันiเป็นเพียง และเรารับจำนวนทั้งหมดแผ่นป้ายทะเบียนที่สมบูรณ์แบบด้วยรูปแบบของเราได้จากข้อสรุปนี้ไปทั่วl[i]*d[i] ขอแสดงถึงการดำเนินงานของการได้รับจำนวนนี้โดยil@d

แม้ว่าวิธีที่ดีที่สุดในการรับรายการเหล่านี้คือการลองชุดค่าผสมและการนับทั้งหมดเราสามารถทำสิ่งนี้ได้อย่างอิสระสำหรับตัวอักษรและตัวเลขดูที่26^4+10^3กรณีและปัญหาแทนที่จะเป็น26^4*10^3 กรณี แต่เราสามารถทำได้ดีกว่า: lเป็นเพียงรายการของสัมประสิทธิ์ของ (x+x^2+...+x^26)^kที่เป็นจำนวนตัวอักษรที่นี่k4

ในทำนองเดียวกันเราได้รับหมายเลขของวิธีการที่จะได้รับผลรวมของตัวเลขในการทำงานของตัวเลขเป็นค่าสัมประสิทธิ์ของk (1+x+...+x^9)^kหากมีมากกว่าหนึ่งการทำงานของตัวเลขเราต้องรวมรายการที่เกี่ยวข้องกับการดำเนินการd1#d2ที่ในตำแหน่งที่iมีค่าเป็นผลรวมของทั้งหมดที่d1[i1]*d2[i2] i1*i2=iนี่คือการแปลง Dirichlet ซึ่งเป็นเพียงผลิตภัณฑ์ถ้าเราตีความรายการเป็นค่าสัมประสิทธิ์ของชุด Dirchlet แต่เราได้ใช้มันเป็นชื่อพหุนาม (อนุกรมไฟไนต์) และไม่มีวิธีที่ดีในการตีความการดำเนินการสำหรับพวกเขา ฉันคิดว่าไม่ตรงกันนี้เป็นส่วนหนึ่งของสิ่งที่ทำให้ยากที่จะหาสูตรง่าย ๆ #ลองใช้มันในหลายชื่ออยู่แล้วและใช้สัญกรณ์เดียวกัน มันง่ายในการคำนวณเมื่อตัวถูกดำเนินการตัวเดียวคือเรามีp(x) # x^k = p(x^k). เมื่อรวมกับความจริงที่ว่ามันเป็นไบลิเนียร์นี่เป็นวิธีที่ดี (แต่ไม่มีประสิทธิภาพมาก) ในการคำนวณ

โปรดทราบว่าkตัวอักษรให้คุณค่าสูงสุดไม่เกิน26kในขณะที่ตัวเลขหลักเดียวสามารถให้ค่าของk 9^kดังนั้นเรามักจะได้รับพลังสูงที่ไม่จำเป็นในdพหุนาม เพื่อกำจัดพวกเราสามารถคำนวณโมดูโล่x^(maxlettervalue+1)ได้ นี้จะช่วยให้ความเร็วขนาดใหญ่ขึ้นและแม้ว่าฉันไม่ได้แจ้งให้ทราบล่วงหน้าทันทีแม้จะช่วยให้การเล่นกอล์ฟเพราะตอนนี้เรารู้ว่าระดับของการdไม่ได้เป็นใหญ่แล้วว่าของlที่ง่ายขีด จำกัด Sumบนในขั้นสุดท้าย เราได้ความเร็วที่ดียิ่งขึ้นโดยทำการmodคำนวณในอาร์กิวเมนต์แรกของValue (ดูความคิดเห็น) และทำการ#คำนวณทั้งหมดในระดับที่ต่ำกว่าจะช่วยเพิ่มความเร็วอย่างไม่น่าเชื่อ แต่เรายังคงพยายามที่จะเป็นคำตอบที่ถูกต้องสำหรับปัญหาการตีกอล์ฟ

ดังนั้นเราจึงได้มีของเราlและและสามารถใช้ในการคำนวณจำนวนแผ่นป้ายทะเบียนที่สมบูรณ์แบบด้วยรูปแบบd นั่นเป็นจำนวนเช่นเดียวกับรูปแบบLDDLLDL LDLLDDLโดยทั่วไปเราสามารถเปลี่ยนลำดับการวิ่งของตัวเลขที่มีความยาวต่างกันตามที่เราต้องการ NrArrangementsให้ความเป็นไปได้ และในขณะที่จะต้องมีตัวอักษรหนึ่งตัวระหว่างตัวเลขวิ่งตัวอักษรอื่น ๆ จะไม่ได้รับการแก้ไข การBinomialนับความเป็นไปได้เหล่านี้

ตอนนี้มันยังคงวิ่งผ่านวิธีที่เป็นไปได้ทั้งหมดของการมีความยาวของตัวเลขการวิ่ง rวิ่งผ่านจำนวนวิ่งcทั้งหมดผ่านจำนวนตัวเลขทั้งหมดทั้งหมดและpผ่านพาร์ติชันทั้งหมดของcด้วยการ rสรุป

จำนวนพาร์ติชั่นทั้งหมดที่เราดูนั้นน้อยกว่าจำนวนพาร์n+1ติชั่นสองพาร์ติชั่นและฟังก์ชันพาร์ติชั่นก็จะเติบโตขึ้นเช่น exp(sqrt(n))กัน ดังนั้นในขณะที่ยังมีวิธีง่าย ๆ ในการปรับปรุงเวลาทำงานโดยการนำผลลัพธ์กลับมาใช้ใหม่ (การทำงานผ่านพาร์ติชันตามลำดับที่แตกต่างกัน) เพื่อการปรับปรุงขั้นพื้นฐานเราจำเป็นต้องหลีกเลี่ยงการดูแต่ละพาร์ติชันแยกกัน

คอมพิวเตอร์มันเร็ว

(p+q)@r = p@r + q@rโปรดสังเกตว่า ด้วยตัวเองสิ่งนี้จะช่วยหลีกเลี่ยงการซ้ำซ้อน แต่เมื่อรวมเข้าด้วยกัน(p+q)#r = p#r + q#rก็หมายความว่าเราสามารถรวมกันได้ด้วยการเติมคำหลายคำที่สอดคล้องกับพาร์ติชั่นที่แตกต่างกัน เราไม่สามารถเพิ่มพวกเขาทั้งหมดได้เพราะเรายังต้องรู้ว่าเราต้องรวมอะไรเข้าด้วยกันซึ่งlเราต้องใช้@ปัจจัยอะไรและปัจจัย#ไหนยังคงเป็นไปได้

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

นี่คือรหัส C ++ ของฉัน:

#include<vector>
#include<algorithm>
#include<iostream>
#include<gmpxx.h>

using bignum = mpz_class;
using poly = std::vector<bignum>;

poly mult(const poly &a, const poly &b){
  poly res ( a.size()+b.size()-1 );
  for(int i=0; i<a.size(); ++i)
    for(int j=0; j<b.size(); ++j)
      res[i+j]+=a[i]*b[j];
  return res;
}

poly extend(const poly &d, const poly &e, int ml, poly &a, int l, int m){
  poly res ( 26*ml+1 );
  for(int i=1; i<std::min<int>(1+26*ml,e.size()); ++i)
    for(int j=1; j<std::min<int>(1+26*ml/i,d.size()); ++j)
      res[i*j] += e[i]*d[j];
  for(int i=1; i<res.size(); ++i)
    res[i]=res[i]*l/m;
  if(a.empty())
    a = poly { res };
  else
    for(int i=1; i<a.size(); ++i)
      a[i]+=res[i];
  return res;
}

bignum f(int n){
  std::vector<poly> dp;
  poly digits (10,1);
  poly dd { 1 };
  dp.push_back( dd );
  for(int i=1; i<n; ++i){
    dd=mult(dd,digits);
    int l=1+26*(n-i);
    if(dd.size()>l)
      dd.resize(l);
    dp.push_back(dd);
  }

  std::vector<std::vector<poly>> a;
  a.reserve(n);

  a.push_back( std::vector<poly> { poly { 0, 1 } } );
  for(int i=1; i<n; ++i)
    a.push_back( std::vector<poly> (1+std::min(i,n+i-i)));
  for(int m=n-1; m>0; --m){
    //    std::cout << "m=" << m << "\n";
    for(int sum=n-m; sum>=0; --sum)
      for(int len=0; len<=std::min(sum,n+1-sum); ++len){
        poly d {a[sum][len]} ;
        if(!d.empty())
          for(int sumn=sum+m, lenn=len+1, e=1;
              sumn+lenn-1<=n;
              sumn+=m, ++lenn, ++e)
            d=extend(d,dp[m],n-sumn,a[sumn][lenn],lenn,e);
      }
  }
  poly let (27,1);
  let[0]=0;
  poly lp { 1 };
  bignum t { 0 };
  for(int sum=n-1; sum>0; --sum){
    lp=mult(lp,let);
    for(int len=1; len<=std::min(sum,n+1-sum); ++len){
      poly &a0 = a[sum][len];
      bignum s {0};
      for(int i=1; i<std::min(a0.size(),lp.size()); ++i)
        s+=a0[i]*lp[i];
      bignum bin;
      mpz_bin_uiui( bin.get_mpz_t(), n-sum+1, len );
      t+=bin*s;
    }
  }
  return t;
}

int main(){
  int n;
  std::cin >> n;
  std::cout << f(n) << "\n" ;
}

สิ่งนี้ใช้ไลบรารี GNU MP ใน Debian libgmp-devติดตั้ง รวบรวมกับg++ -std=c++11 -O3 -o pl pl.cpp -lgmp -lgmpxxคอมไพล์ด้วยโปรแกรมรับอาร์กิวเมนต์จาก stdin echo 100 | time ./plสำหรับระยะเวลาการใช้งาน

ในตอนท้ายa[sum][length][i]จะช่วยให้หลายวิธีในการที่sum ตัวเลขในการทำงานสามารถให้จำนวนlength iในระหว่างการคำนวณที่จุดเริ่มต้นของวงจะช่วยให้หลายวิธีที่สามารถทำได้ด้วยตัวเลขที่มากกว่าm mทุกอย่างเริ่มต้นด้วย a[0][0][1]=1ทุกอย่างเริ่มต้นด้วยโปรดทราบว่านี่เป็นชุดของตัวเลขที่เราต้องการในการคำนวณฟังก์ชันสำหรับค่าที่น้อยลง nดังนั้นที่เกือบจะในเวลาเดียวกันเราสามารถคำนวณค่าทั้งหมดขึ้นอยู่กับ

ไม่มีการเรียกซ้ำดังนั้นเราจึงมีการวนซ้ำหลายระดับที่แน่นอน (ระดับการซ้อนที่ลึกที่สุดคือ 6) แต่ละลูปจะต้องผ่านจำนวนของค่าที่เป็นเชิงเส้นในnกรณีที่แย่ที่สุด ดังนั้นเราต้องการเวลาพหุนามเท่านั้น ถ้าเราดูที่ซ้อนกันiและjวนซ้ำมากขึ้นextendเราจะพบข้อ จำกัด สูงสุดjของแบบฟอร์ม N/iนั่นควรให้ปัจจัยลอการิทึมสำหรับjลูปเท่านั้น วงในสุดf (กับsumnฯลฯ ) คล้ายกัน โปรดจำไว้ว่าเราคำนวณตัวเลขที่เติบโตอย่างรวดเร็ว

โปรดทราบว่าเราจัดเก็บO(n^3)หมายเลขเหล่านี้

จากการทดลองฉันได้รับผลลัพธ์เหล่านี้บนฮาร์ดแวร์ที่เหมาะสม (i5-4590S): f(50)ต้องการหนึ่งวินาทีและ 23 MB f(100)ต้องการ 21 วินาทีและ 166 MB f(200)ต้องการ 10 นาทีและ 1.5 GB และf(300)ต้องการหนึ่งชั่วโมงและ 5.6 GB O(n^5)นี้แสดงให้เห็นความซับซ้อนของเวลาดีกว่า


เนื่องจากนี่คือความท้าทายของรหัสกอล์ฟคำตอบนี้จำเป็นต้องมีการตีกอล์ฟ ขอโทษ
Rɪᴋᴇʀ

1
@Riker ในขณะที่ฉันไม่คิดว่ารหัสของฉัน verbose สุดเหวี่ยงที่จะเริ่มต้นด้วยฉันกอล์ฟมากขึ้นและรับภาระในการกำหนดขนาดของมันเมื่อช่องว่างถูกบีบออก
Christian Sievers

1
@carusocomputing ฉันกลัวว่ามันจะแย่กว่านั้น ฉันจัดการแต่ละกรณีของการกระจายตัวเลขในระหว่างการวิ่งของตัวเลขเช่นมีหนึ่งรันของสามหลักหรือมีหนึ่งรันสองหลักและหนึ่งหลักเดียวหรือมีสามหลักเดียว แต่สำหรับn=5ไม่มี กรณีที่มีตัวเลขสองหลักและตัวเลขสองหลักเพราะเราไม่มีตัวอักษรเพียงพอที่จะแยกตัวเลข นี่คือสิ่งที่สามนอกforลูปไม่: วิ่งผ่านทุกประโยชน์<nพาร์ทิชันของตัวเลข (และฉันเพิ่งรู้ว่าฉันอนุญาตnตัวเลขด้วยโดยโชคดีการเพิ่มประสิทธิภาพอื่นนับเป็น 0)
Christian Sievers

1
@carusocomputing หมายเหตุว่าตัวเลข<n/2, ทุกพาร์ทิชันที่มีประโยชน์ และการคำนวณที่เหลือยังคงใช้เวลาไม่คงที่ หากต้องการดูสิ่งที่เกิดขึ้นคุณสามารถเพิ่มPrint(p,"\n");ที่จุดเริ่มต้นของเนื้อความของfor p...ลูป - ฉันมีความคิดในการใช้ลูปหนึ่งวงน้อยลง แต่มันจะช่วยเฉพาะขนาดโค้ด
Christian Sievers

2
ฉันได้รับความเร็วที่น่าตื่นตาตื่นใจขึ้นโดยการย้ายmod(ซึ่งมีอยู่แล้วช่วยมาก) ลงValue, Value(d mod x^(1+QuoInt(s(l)-1,i-1)),x^(i-1))เปลี่ยนไป เพียงอย่างเดียวที่ช่วยให้การคำนวณf(15)ใน 80 วินาที
Christian Sievers

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