สร้างซีรีส์พจนานุกรมอย่างมีประสิทธิภาพใน Python

ฉันต้องการสร้างชุดคำศัพท์ของตัวเลข โดยที่ผลรวมของตัวเลขแต่ละตัวจะเป็นค่าคงที่ที่กำหนด มันค่อนข้างคล้ายกับ 'ปัญหาผลรวมย่อย' ตัวอย่างเช่น หากฉันต้องการสร้างตัวเลข 4 หลักโดยมีผลรวม = 3 ฉันจะมีชุดดังนี้:

[3 0 0 0]

[2 1 0 0]

[2 0 1 0]

[2 0 0 1]

[1 2 0 0] ... และอื่นๆ

ฉันสามารถทำได้สำเร็จใน Python ด้วยรหัสต่อไปนี้:

import numpy as np

M = 4 # No. of digits
N = 3 # Target sum

a = np.zeros((1,M), int)
b = np.zeros((1,M), int)

a[0][0] = N
jj = 0

while a[jj][M-1] != N:
    ii = M-2
    while a[jj][ii] == 0:
          ii = ii-1
    kk = ii
    if kk > 0:
       b[0][0:kk-1] = a[jj][0:kk-1]
    b[0][kk] = a[jj][kk]-1
    b[0][kk+1] = N - sum(b[0][0:kk+1])
    b[0][kk+2:] = 0
    a = np.concatenate((a,b), axis=0)
    jj += 1

for ii in range(0,len(a)):
    print a[ii]

print len(a)

ฉันไม่คิดว่ามันเป็นวิธีที่มีประสิทธิภาพมากนัก (เนื่องจากฉันเป็นมือใหม่ Python) มันใช้งานได้ดีกับค่า M และ N (‹10) เล็กน้อย แต่ช้ากว่านั้นมาก ฉันต้องการใช้สำหรับ M ~ 100 และ N ~ 6 ฉันจะทำให้โค้ดมีประสิทธิภาพมากขึ้นได้อย่างไร หรือมีวิธีที่ดีกว่าในการเขียนโค้ดหรือไม่


comment
ดูที่การจัดองค์ประกอบจำนวนเต็ม   -  person Joseph Wood    schedule 20.03.2019
comment
ลิงก์ที่ให้มามีข้อมูลที่เป็นประโยชน์มาก อย่างไรก็ตาม ไม่ได้ช่วยในการปรับแต่งอัลกอริทึม   -  person Aman_X    schedule 21.03.2019


คำตอบ (4)


อัลกอริทึมที่มีประสิทธิภาพมากดัดแปลงมาจากหนังสือของ Jorg Arndt "Matters Computational"
(บทที่ 7.2 Co-lexicographic order for compositions into exactly k parts)

n = 4
k = 3

x = [0] * n
x[0] = k

while True:
    print(x)
    v = x[-1]
    if (k==v ):
        break
    x[-1] = 0
    j = -2
    while (0==x[j]):
        j -= 1
    x[j] -= 1
    x[j+1] = 1 + v

[3, 0, 0, 0]
[2, 1, 0, 0]
[2, 0, 1, 0]
[2, 0, 0, 1]
[1, 2, 0, 0]
[1, 1, 1, 0]
[1, 1, 0, 1]
[1, 0, 2, 0]
[1, 0, 1, 1]
[1, 0, 0, 2]
[0, 3, 0, 0]
[0, 2, 1, 0]
[0, 2, 0, 1]
[0, 1, 2, 0]
[0, 1, 1, 1]
[0, 1, 0, 2]
[0, 0, 3, 0]
[0, 0, 2, 1]
[0, 0, 1, 2]
[0, 0, 0, 3]

จำนวนการเรียบเรียงและเวลาเป็นวินาทีสำหรับ Python ธรรมดา (บางทีอาร์เรย์ numpy อาจเร็วกว่า) สำหรับ n=100 และ k = 2,3,4,5 (2.8 ghz Cel-1840)

2  5050 0.040000200271606445
3  171700 0.9900014400482178
4  4421275 20.02204465866089
5  91962520 372.03577995300293
I expect time  2 hours for 100/6 generation

เช่นเดียวกับอาร์เรย์ numpy (x = np.zeros((n,), dtype=int)) ให้ผลลัพธ์ แย่ลง - แต่อาจเป็นเพราะฉันไม่รู้วิธีใช้อย่างถูกต้อง

2  5050 0.07999992370605469
3  171700 2.390003204345703
4  4421275 54.74532389640808

โค้ดเนทิฟ (นี่คือ Delphi คอมไพเลอร์ C/C++ อาจเพิ่มประสิทธิภาพได้ดีกว่า) สร้าง 100/6 ใน 21 วินาที

3  171700  0.012
4  4421275  0.125
5  91962520  1.544
6  1609344100 20.748

นอนไม่หลับจนกว่าจะวัดไม่เสร็จ :)

MSVS VC++: 18 วินาที! (การเพิ่มประสิทธิภาพ O2)

5  91962520 1.466
6  1609344100 18.283

100 ล้านตัวแปรต่อวินาที เสียเวลามากในการตรวจสอบเซลล์ว่าง (เนื่องจากอัตราการเติมมีน้อย) ความเร็วที่ Arndt อธิบายนั้นบรรลุถึงอัตราส่วน k/n ที่สูงกว่า และมีค่าประมาณ 300-500 ล้านตัวแปรต่อวินาที:

n=25, k=15 25140840660 60.981  400 millions per second
person MBo    schedule 21.03.2019
comment
รันโค้ดของคุณเป็น n=100 และ k=6 ซึ่งต้องใช้เวลาในอัลกอริทึมนี้เช่นกัน อาจเป็นวัน - person Shoyeb Sheikh; 21.03.2019
comment
@MBo ฉันพบว่าอัลกอริทึมนี้รวดเร็วและมีประสิทธิภาพจริงๆ สมบูรณ์แบบ! - person Aman_X; 21.03.2019
comment
@Shoyeb Sheikh คุณรู้ไหมว่ามีกี่องค์ประกอบที่มีพารามิเตอร์ดังกล่าว? อัลกอริธึมใดๆ ที่สร้างตัวแปรนับล้านล้านรายการต้องใช้เวลามาก แนวทางที่อธิบายไว้นั้นได้รับการปรับให้เหมาะสมที่สุด - ถูกนำไปใช้ใน C โดยจะสร้างตัวแปร 300 ล้านตัวแปรต่อวินาที (ไม่นับเอาต์พุตหรือการเขียน - ส่วนที่ใช้เวลานานที่สุด) - person MBo; 21.03.2019
comment
@MBo นั่นเป็นส่วนหนึ่งของคำถาม ฮ่าๆ ฉันไม่ได้เพิ่มอะไรลงไป 300 ล้านต่อวินาทีนั้นมีประสิทธิภาพมาก เยี่ยมมาก ! - person Shoyeb Sheikh; 21.03.2019
comment
@Shoyeb Sheikh ใช่ฉันสังเกตเห็นว่าผู้เขียนคาดว่าจะได้รับตัวแปรมากมาย - เป็นไปได้ที่จะคำนวณ แต่มันค่อนข้างยากที่จะจัดเก็บและใช้ข้อมูลจำนวนมหาศาลเช่นนี้ - person MBo; 21.03.2019
comment
@MBo นั่นเป็นการเปรียบเทียบที่น่าสนใจจริงๆ ฉันลองใช้ด้วยสำหรับ n=100 และ k=5 และใช้เวลาประมาณ 97 วินาที BTW คุณรู้หรือไม่ว่าการคำนวณสามารถขนานกันได้หรือไม่? - person Aman_X; 22.03.2019
comment
ไม่ อัลกอริธึมนี้ไม่ถือว่ามีความขนาน คุณจะใช้ข้อมูลผลลัพธ์อย่างไร - person MBo; 22.03.2019
comment
ฉันใช้มันเพื่อสร้างพื้นฐานเพื่อแสดงฟังก์ชันคลื่นและการคำนวณเพิ่มเติม - person Aman_X; 22.03.2019
comment
แต่... 1.6 ล้านล้านตัวแปรเหรอ? - person MBo; 22.03.2019
comment
คือ...นั่นจะเป็นกรณีที่รุนแรง โดยปกติแล้ว n จะถูกจำกัดไว้ที่ 64 มีหลายวิธีในการลดจำนวนตัวแปรเพิ่มเติมโดยใช้สมมาตรของระบบ เป็นต้น - person Aman_X; 22.03.2019

คำแนะนำของฉัน:

  1. เขียนใหม่เป็นตัวสร้างโดยใช้ yield แทนที่จะเป็นลูปที่เชื่อมตัวแปรโกลบอลเข้าด้วยกันในการวนซ้ำแต่ละครั้ง
  2. เก็บผลรวมต่อเนื่องแทนการคำนวณผลรวมของชุดย่อยบางส่วนของการแสดงตัวเลขในอาร์เรย์
  3. ดำเนินการกับอินสแตนซ์เดียวของการแสดงหมายเลขการทำงานของคุณ แทนที่จะเชื่อมต่อสำเนาของอินสแตนซ์ดังกล่าวกับตัวแปรชั่วคราวในการวนซ้ำแต่ละครั้ง

หมายเหตุ ไม่มีคำสั่งใดที่มีความหมายโดยนัย

person pkfm    schedule 21.03.2019

ฉันมีทางออกที่ดีกว่าโดยใช้ itertools ดังนี้

from itertools import product
n = 4 #number of elements
s = 3 #sum of elements
r = []
for x in range(n):
    r.append(x)
result = [p for p in product(r, repeat=n) if sum(p) == s]
print(len(result))
print(result)

ฉันกำลังบอกว่าวิธีนี้ดีกว่าเพราะระบบของฉันใช้เวลา 0.1 วินาที ในขณะที่โค้ดที่มี numpy ของคุณใช้เวลา 0.2 วินาที

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

ป้อนคำอธิบายลิงก์ที่นี่

แต่เท่าที่ n=100 และ s=6 โค้ดนี้ต้องใช้เวลาในการพิจารณาชุดค่าผสมทั้งหมด ฉันคิดว่าจะใช้เวลาหลายวันในการคำนวณผลลัพธ์

person Shoyeb Sheikh    schedule 21.03.2019
comment
จริงๆ แล้ว ฉันต้องการปรับปรุงวิธีการเข้ารหัสอัลกอริทึม (หรือโค้ดอัลกอริทึมที่มีประสิทธิภาพมากขึ้น) เพื่อคำนวณ เฉพาะที่จำเป็น ชุดค่าผสม เท่าที่ฉันเข้าใจ รหัสของคุณจะตรวจสอบ ชุดค่าผสมที่เป็นไปได้ทั้งหมด กับผลรวมที่กำหนด คุณสามารถกำหนดเวลาทั้งรหัสสำหรับ M=10, N=3 ได้หรือไม่ - person Aman_X; 21.03.2019
comment
ใช้เวลาประมาณหนึ่งชั่วโมงสำหรับ M=10 และ N=3 และมันยังคงทำงานอยู่ ฉันคิดว่า MBo มีคำตอบที่ดีกว่า - person Shoyeb Sheikh; 21.03.2019
comment
ไม่ คุณตรวจสอบแล้วแจ้งให้เราทราบ - person Shoyeb Sheikh; 21.03.2019
comment
โอเค ฉันทำอย่างนั้น ใช้เวลาไม่ถึงหนึ่งวินาที - person Aman_X; 22.03.2019

ฉันพบวิธีแก้ปัญหาโดยใช้ itertools เช่นกัน (ที่มา: https://bugs.python.org/msg144273 ). รหัสดังต่อไปนี้:

import itertools
import operator

def combinations_with_replacement(iterable, r):
    # combinations_with_replacement('ABC', 2) --> AA AB AC BB BC CC
    pool = tuple(iterable)
    n = len(pool)
    if not n and r:
        return
    indices = [0] * r
    yield tuple(pool[i] for i in indices)
    while True:
        for i in reversed(range(r)):
            if indices[i] != n - 1:
                break
        else:
            return
        indices[i:] = [indices[i] + 1] * (r - i)
        yield tuple(pool[i] for i in indices)

int_part = lambda n, k: (tuple(map(c.count, range(k))) for c in combinations_with_replacement(range(k), n))
for item in int_part(3,4): print(item)
person Aman_X    schedule 18.11.2019