อัลกอริทึมในการคำนวณจำนวนตัวหารของจำนวนที่กำหนด


177

อะไรคืออัลกอริทึมที่เหมาะสมที่สุด (ประสิทธิภาพที่ฉลาด) ในการคำนวณจำนวนตัวหารของจำนวนที่กำหนด?

มันจะดีถ้าคุณสามารถให้ pseudocode หรือลิงค์ไปยังตัวอย่าง

แก้ไข: คำตอบทั้งหมดเป็นประโยชน์มากขอบคุณ ฉันกำลังใช้ Sieve of Atkin จากนั้นฉันจะใช้สิ่งที่คล้ายกับที่ Jonathan Leffler ระบุ ลิงก์ที่โพสต์โดย Justin Bozonier มีข้อมูลเพิ่มเติมเกี่ยวกับสิ่งที่ฉันต้องการ


เมื่อพิจารณาจากจำนวนปัจจัยที่คลุมเครือ ฉันเดาว่าคุณกำลังมองหาจำนวนตัวหารเฉพาะที่ไม่เหมือนใครเพราะถ้าคุณไม่ต้องการให้ฉันเขียนโค้ดโปรแกรมจะคืนค่า 1 เสมอหากจำนวนตัวประกอบเป็น 1 และ 2 ถ้าเป็นอย่างอื่น 0 อาจต้องมีการเปลี่ยนแปลง ...
Justin Bozonier

@sker: มีช่วงของค่าที่คุณต้องการตัวหาร การคำนวณปัจจัยมีหลายวิธีและแต่ละวิธีมีความเหมาะสมกับช่วงเฉพาะ
Ande Turner

2
นี่คือปัญหาที่น่าสนใจที่เกี่ยวข้องprojecteuler.net/problem=12
daniloquio

1
Sieve ที่ไร้เดียงสาของ Atkin แม้จะมาจากบทความ Wikipedia ที่แก้ไขแล้วจะไม่เร็วกว่า Sieve of Eratosthen ของล้อเฟืองที่มากที่สุดจนถึงขีด จำกัด การใช้งานไม่ได้มาก .. ดำเนินการโดยพันธมิตรแอทคินของสเตนมันเป็นเรื่องธรรมดาความรู้อินเทอร์เน็ตไม่ถูกต้องที่การศึกษาของพวกเขาได้รับการพิสูจน์ SoA เร็วขึ้น แต่พวกเขาเทียม จำกัด การเพิ่มประสิทธิภาพของรัฐวิสาหกิจที่ใช้ในการสั่งซื้อเพื่อพิสูจน์เรื่องนี้ดูคำตอบ SoA ของฉันสำหรับคำอธิบายเพิ่มเติม
GordonBGood

คำตอบ:


78

Dmitriy นั้นถูกต้องที่คุณจะต้องการ Sieve of Atkin เพื่อสร้างรายชื่อที่ดี แต่ฉันไม่เชื่อว่าจะดูแลปัญหาทั้งหมด ตอนนี้คุณมีรายการของช่วงเวลาที่คุณจะต้องดูว่าจำนวนช่วงเวลาเหล่านั้นทำหน้าที่เป็นตัวหาร (และความถี่)

นี่คือบางไพ ธ อนสำหรับ algo ดูที่นี่และค้นหา "หัวเรื่อง: คณิตศาสตร์ - อัลกอริธึม divisors" เพียงนับจำนวนรายการในรายการแทนที่จะส่งคืนอย่างไรก็ตาม

นี่คือ Dr. Mathที่อธิบายว่าคุณจำเป็นต้องทำอะไรในทางคณิตศาสตร์

โดยพื้นฐานแล้วมันจะลดลงถ้าหมายเลขของคุณnคือ:
n = a^x * b^y * c^z
(โดยที่ a, b, และ c คือตัวหารหลักของ n และ x, y, และ z คือจำนวนครั้งที่ตัวหารซ้ำแล้วซ้ำอีก) จำนวนรวมของตัวหารทั้งหมดคือ:
(x + 1) * (y + 1) * (z + 1).

แก้ไข: BTW, เพื่อหา a, b, c, ฯลฯ คุณจะต้องทำสิ่งที่เป็นโลภโลภหากฉันเข้าใจสิ่งนี้อย่างถูกต้อง เริ่มต้นด้วยตัวหารที่ใหญ่ที่สุดของคุณและคูณด้วยตัวมันเองจนกว่าการคูณจะมากกว่าจำนวน n จากนั้นย้ายไปยังตัวคูณที่ต่ำที่สุดถัดไปและคูณจำนวนไพร์มก่อนหน้าจำนวนครั้งที่ถูกคูณด้วยไพร์มปัจจุบันและทำการคูณด้วยไพร์มจนกว่าจะถัดไปจะเกิน n ... ฯลฯ ติดตามจำนวนครั้งที่คุณคูณ ตัวหารด้วยกันและใช้ตัวเลขเหล่านั้นลงในสูตรด้านบน

ไม่แน่ใจ 100% เกี่ยวกับคำอธิบายตัวตนของฉัน แต่ถ้าไม่ใช่นั่นก็คล้ายกัน


1
หากคุณกำลังพิจารณาจำนวนมากคุณไม่จำเป็นต้องดูรายการสำคัญ คุณต้องการกำจัดช่วงของความเป็นไปได้ทั้งหมดโดยเร็วที่สุด! ดูคำตอบของฉันมากขึ้น
user11318

ฉันรู้ว่านี่เป็น 2 ปีที่ผ่านมา แต่การเชื่อมโยงหลามหลามของคุณเสียหายเกิดขึ้นเพื่อทราบว่ามันอยู่ที่ไหนตอนนี้?
jb

2
ดังนั้นn = (a^x * b^y * c^z)-(x + 1) * (y + 1) * (z + 1)กฎ
SIslam

1
ตามที่ @Shashank กล่าวว่าอัลกอริทึมในส่วน "แก้ไข:" ผิด: สมมติว่า n = 45 = 3 * 3 * 5 ตัวหารหลักที่ใหญ่ที่สุดคือ 5 แต่การคูณด้วยตัวเองจนกว่ามันจะเกิน n จะทำให้อัลกอริทึมรายงานว่ามันมีสำเนาของปัจจัย 5 5 (ตั้งแต่ 5 * 5 = 25 <45)
j_random_hacker

1
'Sieve of Atkin' มีความซับซ้อนแบบรันไทม์ของO (N / log (log (N)))ที่ดีที่สุด กำลังตรวจสอบตัวหารที่เป็นไปได้ทั้งหมดตั้งแต่ 1 ... Sqrt (n) มีความซับซ้อนรันไทม์ของO (Sqrt (N))ซึ่งเหนือกว่ามาก คำตอบนี้เป็นที่ยอมรับได้อย่างไร
le_m

47

มีเป็นจำนวนมากเทคนิคมากขึ้นในการแฟกว่าตะแกรงของแอทคิน ตัวอย่างเช่นสมมติว่าเราต้องการตัวประกอบ 5893 ทีนี้ sqrt ของมันคือ 76.76 ... ทีนี้เราจะลองเขียน 5893 เป็นผลคูณของกำลังสอง Well (77 * 77 - 5893) = 36 ซึ่งเท่ากับ 6 กำลังสองดังนั้น 5893 = 77 * 77 - 6 * 6 = (77 + 6) (77-6) = 83 * 71 ถ้านั่นไม่ได้ผลเราจะดูว่า 78 * 78 - 5893 เป็นสี่เหลี่ยมจัตุรัสที่สมบูรณ์แบบหรือไม่ และอื่น ๆ ด้วยเทคนิคนี้คุณสามารถทดสอบปัจจัยที่อยู่ใกล้กับรากที่สองของ n ได้เร็วกว่ามากโดยการทดสอบเฉพาะช่วงเวลา หากคุณรวมเทคนิคนี้เพื่อแยกแยะจำนวนเฉพาะกับตะแกรงคุณจะมีวิธีแฟคตอริ่งที่ดีกว่าการใช้ตะแกรงเพียงอย่างเดียว

และนี่เป็นเพียงหนึ่งในเทคนิคจำนวนมากที่ได้รับการพัฒนา นี่เป็นสิ่งที่ค่อนข้างง่าย มันจะใช้เวลานานในการเรียนรู้พูดทฤษฎีจำนวนมากพอที่จะเข้าใจเทคนิคแฟคตอริ่งตามเส้นโค้งรูปไข่ (ฉันรู้ว่ามีอยู่ฉันไม่เข้าใจ)

ดังนั้นหากคุณไม่ได้จัดการกับจำนวนเต็มเล็ก ๆ ฉันจะไม่พยายามแก้ปัญหาด้วยตัวเอง แต่ฉันจะพยายามหาวิธีที่จะใช้บางอย่างเช่นไลบรารีPARIที่มีวิธีการแก้ปัญหาที่มีประสิทธิภาพสูงอยู่แล้ว เมื่อถึงตอนนั้นฉันสามารถแยกตัวเลขสุ่ม 40 หลักเช่น 124321342332143213122323434312213424231341 ในเวลาประมาณ 0.05 วินาที (การแยกตัวประกอบในกรณีที่คุณสงสัยคือ 29 * 439 * 1321 * 157907 * 284749 * 33843676813 * 4857795469949 ฉันค่อนข้างมั่นใจว่ามันไม่ได้ใช้ตะแกรงของ Atkin ... )


1
เทคนิคของคุณฉลาดมาก แต่มันไม่ได้บอกฉันว่ามีจำนวนเท่าไร
sker

23
เมื่อคุณมีการแยกตัวประกอบเฉพาะแล้วหาว่ามีกี่ปัจจัยที่ตรงไปตรงมา สมมติว่าปัจจัยสำคัญคือ p1, p2, ... , pk และพวกมันซ้ำ m1, m2, ... , mk คูณ จากนั้นจะมีปัจจัย (1 + m1) (1 + m2) ... (1 + mk)
user11318

ตะแกรงที่น่าสนใจคือตะแกรงกำลังสอง สิ่งนี้ใช้ทฤษฎีจำนวน - สมการกำลังสองและพีชคณิตเชิงเส้นบางส่วน ฉันเรียนรู้พอที่จะใช้มันในหลักสูตรทฤษฎีจำนวนปีที่ 2 ที่มหาวิทยาลัย
แทนเนอร์

33

@Yasky

ฟังก์ชันตัวหารของคุณมีข้อบกพร่องซึ่งมันทำงานไม่ถูกต้องสำหรับสี่เหลี่ยมที่สมบูรณ์แบบ

ลอง:

int divisors(int x) {
    int limit = x;
    int numberOfDivisors = 0;

    if (x == 1) return 1;

    for (int i = 1; i < limit; ++i) {
        if (x % i == 0) {
            limit = x / i;
            if (limit != i) {
                numberOfDivisors++;
            }
            numberOfDivisors++;
        }
    }

    return numberOfDivisors;
}

6
จะไม่ (x% i) ทำให้หารด้วยศูนย์เมื่อ i = 0 หรือไม่ ฉันควร = 1..limit?
rhu

@rhu การตรวจสอบ 0 นั้นไม่มีจุดหมายเลยเพราะ 0 ไม่ใช่ปัจจัยของจำนวนใด ๆ
EJoshuaS - Reinstate Monica

29

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

นี่คือโค้ดบางส่วนที่แม้ว่าแฮ็กเกอร์เล็กน้อยจะเร็วขึ้นมาก:

import operator
# A slightly efficient superset of primes.
def PrimesPlus():
  yield 2
  yield 3
  i = 5
  while True:
    yield i
    if i % 6 == 1:
      i += 2
    i += 2
# Returns a dict d with n = product p ^ d[p]
def GetPrimeDecomp(n):
  d = {}
  primes = PrimesPlus()
  for p in primes:
    while n % p == 0:
      n /= p
      d[p] = d.setdefault(p, 0) + 1
    if n == 1:
      return d
def NumberOfDivisors(n):
  d = GetPrimeDecomp(n)
  powers_plus = map(lambda x: x+1, d.values())
  return reduce(operator.mul, powers_plus, 1)

ป.ล.ที่ทำงานรหัสหลามเพื่อแก้ปัญหานี้


11

นี่คืออัลกอริทึม O (sqrt (n)) แบบส่งตรง ฉันใช้สิ่งนี้เพื่อแก้ออยเลอร์โครงการ

def divisors(n):
    count = 2  # accounts for 'n' and '1'
    i = 2
    while i ** 2 < n:
        if n % i == 0:
            count += 2
        i += 1
    if i ** 2 == n:
        count += 1
    return count

แต่ทำไมคุณเพิ่มจำนวน 2 เสมอ ... มีทฤษฎีบทที่คุณใช้หรือไม่
SummerCode

3
เพราะคุณจะเข้าร่วมจนกว่าจะถึง sqrt (n) ตัวอย่างเช่น: หากคุณกำลังพยายามหาตัวหารทั้งหมดสำหรับ 36 - คุณจะนับจาก 2 ถึง 6 คุณรู้ว่า 1 & 36,2 & 18, 3 & 12, 4 & 9, 6,6 เป็นตัวหารทั้งหมดและพวกมันมาเป็นคู่
Antony Thomas

2
ขอบคุณแอนโทนี่มากตอนนี้ฉันเข้าใจแล้ว: D! ภาคผนวกเล็ก ๆ : ฉันคิดว่ามันควรจะรักษาค่า sqrt (n) แยกต่างหากเพราะตอนนี้มันต้องคำนึงถึงสองครั้งแทนที่จะเป็นหนึ่งฉันคิดว่า
SummerCode

ในขณะที่ O (sqrt (n)) ไม่ได้เลวร้ายเกินไป แต่ก็ไม่เหมาะสม การคำนวณการแยกตัวประกอบที่สำคัญสามารถทำได้เร็วกว่าและเพียงพอที่จะคำนวณจำนวนตัวหาร
le_m

ในการวนซ้ำแต่ละครั้งคุณต้องคำนวณi²จะไม่เร็วกว่าการเปรียบเทียบ i กับ√n (คำนวณเพียงครั้งเดียว) หรือไม่
Yukulélé

10

คำถามที่น่าสนใจนี้ยากกว่าที่คิดและยังไม่ได้รับคำตอบ คำถามนี้สามารถแยกออกเป็น 2 คำถามที่แตกต่างกันมาก

1 ที่ได้รับ N ค้นหารายการ L ของปัจจัยสำคัญของ N

2 กำหนด L คำนวณจำนวนชุดค่าผสมที่ไม่ซ้ำกัน

คำตอบทั้งหมดที่ฉันเห็นตอนนี้อ้างถึง # 1 และไม่ได้พูดถึงมันไม่สามารถหาได้ง่ายสำหรับจำนวนมหาศาล สำหรับ N ที่มีขนาดปานกลางถึงตัวเลข 64- บิตมันเป็นเรื่องง่าย สำหรับ N ขนาดใหญ่ปัญหาแฟคตอริ่งสามารถใช้ "ตลอดไป" การเข้ารหัสคีย์สาธารณะขึ้นอยู่กับสิ่งนี้

คำถาม # 2 ต้องการการสนทนาเพิ่มเติม หาก L มีตัวเลขที่ไม่ซ้ำกันเท่านั้นมันเป็นการคำนวณอย่างง่ายโดยใช้สูตรการผสมสำหรับการเลือกวัตถุ k จาก n รายการ ที่จริงแล้วคุณต้องรวมผลลัพธ์จากการใช้สูตรในขณะที่เปลี่ยน k จาก 1 เป็น sizeof (L) อย่างไรก็ตาม L มักจะมีเหตุการณ์เกิดขึ้นหลายครั้งหลายช่วงเวลา ตัวอย่างเช่น L = {2,2,2,3,3,5} เป็นตัวประกอบของ N = 360 ตอนนี้ปัญหานี้ค่อนข้างยาก!

กำลังเรียกคืน # 2 ซึ่งได้รับคอลเล็กชัน C ที่มีรายการ k เช่นรายการ a มีรายการซ้ำซ้อนและรายการ b มีรายการซ้ำเป็นต้นมีการรวมที่ไม่ซ้ำกันของรายการ 1 ถึง k-1 จำนวนเท่าใด ตัวอย่างเช่น {2}, {2,2}, {2,2,2}, {2,3}, {2,2,3,3} แต่ละครั้งจะต้องเกิดขึ้นครั้งเดียวและครั้งเดียวถ้า L = {2,2 , 2,3,3,5} แต่ละกลุ่มย่อยที่ไม่ซ้ำกันดังกล่าวเป็นตัวหารที่ไม่ซ้ำกันของ N โดยการคูณรายการในคอลเลกชันย่อย


นี่คือการเชื่อมโยงไปรหัสเทียมบางอย่างสำหรับปัญหาคล้ายกับ 2. answers.google.com/answers/threadview/id/392914.html
mR_fr0g

3
คำถาม # 2 มีทางออกที่รู้จักกันดี สำหรับตัวประกอบของ {p_i, k_i} ซึ่งp_iเป็นปัจจัยสำคัญของตัวเลขโดยมีหลายหลากจำนวนรวมของตัวหารของตัวเลขที่เป็นk_i (k_1+1)*(k_2+1)*...*(k_n+1)ฉันเดาว่าคุณรู้เรื่องนี้แล้ว แต่ฉันเขียนลงไปเพื่อประโยชน์ถ้าผู้อ่านแบบสุ่มที่นี่
Will Ness

9

คำตอบสำหรับคำถามของคุณขึ้นอยู่กับขนาดของจำนวนเต็มเป็นอย่างมาก วิธีการสำหรับตัวเลขขนาดเล็กเช่นน้อยกว่า 100 บิตและสำหรับตัวเลข ~ 1,000 บิต (เช่นที่ใช้ในการเข้ารหัส) จะแตกต่างกันโดยสิ้นเชิง


6

เพียงแค่บรรทัดเดียว
ฉันคิดอย่างถี่ถ้วนเกี่ยวกับคำถามของคุณและฉันได้พยายามเขียนโค้ดที่มีประสิทธิภาพและมีประสิทธิภาพสูงในการพิมพ์ตัวหารทั้งหมดของตัวเลขที่กำหนดบนหน้าจอเราต้องการโค้ดเพียงบรรทัดเดียว! (ใช้ตัวเลือก -std = c99 ขณะรวบรวมผ่าน gcc)

for(int i=1,n=9;((!(n%i)) && printf("%d is a divisor of %d\n",i,n)) || i<=(n/2);i++);//n is your number

สำหรับการค้นหาจำนวนตัวหารคุณสามารถใช้ฟังก์ชันที่รวดเร็วมากดังต่อไปนี้ (ทำงานอย่างถูกต้องสำหรับหมายเลขจำนวนเต็มทั้งหมดยกเว้น 1 และ 2)

int number_of_divisors(int n)
{
    int counter,i;
    for(counter=0,i=1;(!(n%i) && (counter++)) || i<=(n/2);i++);
    return counter;
}

หรือถ้าคุณปฏิบัติต่อตัวเลขที่กำหนดเป็นตัวหาร (ทำงานอย่างถูกต้องสำหรับจำนวนเต็มทั้งหมดยกเว้น 1 และ 2)

int number_of_divisors(int n)
{
    int counter,i;
    for(counter=0,i=1;(!(n%i) && (counter++)) || i<=(n/2);i++);
    return ++counter;
}

หมายเหตุ: ฟังก์ชั่นทั้งสองข้างต้นทำงานได้อย่างถูกต้องสำหรับเลขจำนวนเต็มบวกทั้งหมดยกเว้นหมายเลข 1 และ 2 ดังนั้นจึงสามารถใช้งานได้กับทุกหมายเลขที่มากกว่า 2 แต่ถ้าคุณต้องการครอบคลุม 1 และ 2 คุณสามารถใช้หนึ่งในฟังก์ชั่นต่อไปนี้ ช้าลง)

int number_of_divisors(int n)
{
    int counter,i;
    for(counter=0,i=1;(!(n%i) && (counter++)) || i<=(n/2);i++);
    if (n==2 || n==1)
    {
    return counter;
    }
    return ++counter;
}

หรือ

int number_of_divisors(int n)
{
    int counter,i;
for(counter=0,i=1;(!(i==n) && !(n%i) && (counter++)) || i<=(n/2);i++);
    return ++counter;
}

ขนาดเล็กสวยงาม :)


5

The sieve of Atkin เป็นรุ่นที่ได้รับการปรับปรุงให้ดีที่สุดของ Eratosthenes ซึ่งให้ตัวเลขที่สำคัญทั้งหมดเป็นจำนวนเต็ม คุณควรจะสามารถ google นี้เพื่อดูรายละเอียดเพิ่มเติม

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

ขั้นตอนพื้นฐานในการคำนวณตัวหารสำหรับตัวเลข (n) คือ [นี่คือ pseudocode ที่ถูกแปลงจากรหัสจริงดังนั้นฉันหวังว่าฉันไม่ได้แนะนำข้อผิดพลาด]:

for z in 1..n:
    prime[z] = false
prime[2] = true;
prime[3] = true;

for x in 1..sqrt(n):
    xx = x * x

    for y in 1..sqrt(n):
        yy = y * y

        z = 4*xx+yy
        if (z <= n) and ((z mod 12 == 1) or (z mod 12 == 5)):
            prime[z] = not prime[z]

        z = z-xx
        if (z <= n) and (z mod 12 == 7):
            prime[z] = not prime[z]

        z = z-yy-yy
        if (z <= n) and (x > y) and (z mod 12 == 11):
            prime[z] = not prime[z]

for z in 5..sqrt(n):
    if prime[z]:
        zz = z*z
        x = zz
        while x <= limit:
            prime[x] = false
            x = x + zz

for z in 2,3,5..n:
    if prime[z]:
        if n modulo z == 0 then print z

5

คุณอาจลองอันนี้ มันค่อนข้างแฮ็ค แต่ก็เร็วพอสมควร

def factors(n):
    for x in xrange(2,n):
        if n%x == 0:
            return (x,) + factors(n/x)
    return (n,1)

2
ในขณะที่ฟังก์ชั่นนี้ให้การสลายตัวของตัวประกอบตัวประกอบที่สำคัญของ n ในเวลาที่เหมาะสมมันคือ a) ไม่เหมาะสมและ b) ไม่คำนวณจำนวนตัวหารของจำนวนที่กำหนดตามคำถามของ OP
le_m

และจะไม่ทำงานสำหรับคนจำนวนมากเนื่องจากการสอบถามซ้ำ
whackamadoodle3000

แม้ว่าสิ่งนี้จะไม่ดีที่สุดและไม่ใช่ปัจจัยการนับแต่จริงๆแล้วมันแสดงรายการความเรียบง่ายและความสวยงามของสิ่งนี้น่าทึ่งและรวดเร็วพอสมควร ^^
Gaurav Singhal

5

เมื่อคุณมีการแยกตัวประกอบเฉพาะมีวิธีหาจำนวนตัวหาร เพิ่มหนึ่งเลขชี้กำลังสำหรับแต่ละเลขชี้กำลังในแต่ละปัจจัยแล้วคูณเลขชี้กำลังด้วยกัน

ตัวอย่างเช่น: 36 การแยกตัวประกอบเฉพาะ: 2 ^ 2 * 3 ^ 2 ตัวหาร: 1, 2, 3, 4, 6, 9, 12, 18, 18, 36 จำนวนตัวหาร: 9

เพิ่มหนึ่งเลขชี้กำลังสำหรับเลขชี้กำลัง 2 ^ 3 * 3 ^ 3 เลขชี้กำลังคูณ: 3 * 3 = 9


3

ก่อนที่คุณจะยอมรับวิธีแก้ปัญหาให้พิจารณาว่าวิธีการของ Sieve อาจไม่ใช่คำตอบที่ดีในกรณีทั่วไป

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

1) ในขณะที่มนุษย์ใช้เวลาในการแบ่งพวกเขาจะรวดเร็วมากในคอมพิวเตอร์ - คล้ายกับค่าใช้จ่ายในการค้นหาคำตอบ

2) ถ้าคุณไม่มีตารางเฉพาะคุณสามารถสร้างลูปที่ทำงานทั้งหมดในแคช L1 ทำให้เร็วขึ้น


3

นี่คือทางออกที่มีประสิทธิภาพ:

#include <iostream>
int main() {
  int num = 20; 
  int numberOfDivisors = 1;

  for (int i = 2; i <= num; i++)
  {
    int exponent = 0;
    while (num % i == 0) {
        exponent++; 
        num /= i;
    }   
    numberOfDivisors *= (exponent+1);
  }

  std::cout << numberOfDivisors << std::endl;
  return 0;
}

2

ตัวหารทำสิ่งที่งดงาม: พวกเขาแบ่งอย่างสมบูรณ์ หากคุณต้องการที่จะตรวจสอบจำนวนหารสำหรับตัวเลขมันอย่างชัดเจนไม่ซ้ำซ้อนช่วงคลื่นความถี่ทั้งหมดn 1...nผมไม่เคยทำใด ๆ การวิจัยในเชิงลึกสำหรับการนี้ แต่ฉันจะแก้ไขโครงการออยเลอร์ปัญหา 12 เบอร์สามเหลี่ยม โซลูชันของฉันสำหรับการทดสอบตัวหารมากกว่า 500 ตัวใช้เวลา 30,9504 ไมโครวินาที (~ 0.3 วินาที) ฉันเขียนฟังก์ชันตัวหารนี้สำหรับการแก้ปัญหา

int divisors (int x) {
    int limit = x;
    int numberOfDivisors = 1;

    for (int i(0); i < limit; ++i) {
        if (x % i == 0) {
            limit = x / i;
            numberOfDivisors++;
        }
    }

    return numberOfDivisors * 2;
}

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

สุขสันต์วันหยุด.


1
คุณมีการหารด้วย 0 ในการทำซ้ำครั้งแรกที่นี่
barfoon

แต่น่าเสียดายที่ไม่ได้. ++ ฉันแตกต่างจาก i ++ (ซึ่งจะส่งผลให้เกิดข้อผิดพลาดหารด้วยศูนย์)
iGbanam

ฉันเขียนฟังก์ชันของคุณใน PHP และใช้งาน - นี่คือสิ่งที่ฉันได้รับ - i.minus.com/iKzuSXesAkpbp.png
barfoon

ด้วยเหตุผลแปลก ๆ สิ่งนี้ใช้ได้กับฉันอย่างไม่มีที่ติ โอ้ดีฉันไม่ดี เริ่มต้นnumberOfDivisorsและตัววนซ้ำที่ 1; สิ่งนี้ควรกำจัดการหารด้วยข้อผิดพลาดที่เป็นศูนย์
iGbanam

1
อัลกอริทึมของคุณใช้ไม่ได้กับสี่เหลี่ยมที่สมบูรณ์แบบ ตัวอย่างเช่นจะคืนค่า 4 สำหรับอินพุต x = 4 เพราะมันจะนับ 2 สองครั้ง ... 1, 2, 2, 4 คำตอบควรเป็น 3: 1,2,4
Michael

1

คุณต้องการตะแกรงของ Atkin อธิบายไว้ที่นี่: http://en.wikipedia.org/wiki/Sieve_of_Atkin


1
นั่นจะทำให้คุณได้จำนวนที่ต่ำกว่าจำนวนที่คุณให้ไว้ - แต่ไม่มีอะไรรับประกันได้ว่าช่วงเวลาเหล่านั้นจะเป็นตัวหารหรือไม่? (ถ้าฉันไม่มีอะไรหายไป)
Andrew Edgecombe

มันคือการกระโดดอย่างรวดเร็วจากที่นี่เพื่อค้นหาช่วงเวลาทั้งหมด <sqrt (N) ที่แบ่งเท่า ๆ กัน N.
SquareCog

1
อาจเป็นการก้าวกระโดดอย่างรวดเร็ว แต่การทดสอบช่วงเวลาทั้งหมด <sqrt (N) ยังคงเป็นเทคนิคการแยกตัวประกอบที่ไม่ดีไม่ว่าคุณจะพบพวกเขาได้อย่างมีประสิทธิภาพได้อย่างไร มีหลายวิธีในการปรับปรุง
user11318

การทดสอบเฉพาะช่วงคือ O (N) มันกำลังค้นหาช่วงเวลาที่เป็นส่วนที่ยาก แต่ถึงแม้จะมีตะแกรง eratosthenes ที่ไม่ได้ทำให้สุกคุณก็ยังสามารถหาจำนวนเฉพาะทั้งหมดภายในไม่กี่ล้านครั้งภายในไม่กี่วินาที ที่ครอบคลุมจำนวน 64b ใด ๆ และผมมั่นใจว่าเราไม่ได้พูดถึงสิ่งที่แฟระดับการเข้ารหัสลับที่นี่
แมทธิว Scharley

1

วิธีหมายเลขเฉพาะชัดเจนมากที่นี่ P [] เป็นรายการหมายเลขเฉพาะน้อยกว่าหรือเท่ากับ sq = sqrt (n);

for (int i = 0 ; i < size && P[i]<=sq ; i++){
          nd = 1;
          while(n%P[i]==0){
               n/=P[i];
               nd++;
               }
          count*=nd;
          if (n==1)break;
          }
      if (n!=1)count*=2;//the confusing line :D :P .

     i will lift the understanding for the reader  .
     i now look forward to a method more optimized  .

1

ตำราทฤษฎีจำนวนเรียกใช้ฟังก์ชันการนับตัวหาร ข้อเท็จจริงแรกที่น่าสนใจคือมันมีหลายหลากเช่น τ (ab) = τ (a) τ (b) เมื่อ a และ b ไม่มีปัจจัยทั่วไป (หลักฐาน: ตัวหารแต่ละตัวของ a และ b ให้ตัวหารที่แตกต่างกันของ ab)

ตอนนี้ทราบว่าสำหรับ pa prime, τ (p ** k) = k + 1 (พลังของ p) ดังนั้นคุณสามารถคำนวณτ (n) ได้อย่างง่ายดายจากการแยกตัวประกอบ

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

  1. ทดสอบว่าตัวเลขนั้นดีหรือไม่ (เร็ว)
  2. ถ้าเป็นเช่นนั้นส่งคืน 2
  3. มิฉะนั้นให้แยกจำนวน (ช้าถ้าหลายตัวคูณตัวใหญ่)
  4. คำนวณτ (n) จากการแยกตัวประกอบ

1

ต่อไปนี้เป็นโปรแกรม C เพื่อค้นหาจำนวนตัวหารของจำนวนที่กำหนด

ความซับซ้อนของอัลกอริทึมด้านบนคือ O (sqrt (n))

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

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

โปรดทราบว่าการจัดเก็บตัวแปรส่วนบนในตัวแปรแยกต่างหากยังช่วยประหยัดเวลาคุณไม่ควรเรียกฟังก์ชัน sqrt ในส่วนเงื่อนไขของ for for loop ซึ่งจะช่วยประหยัดเวลาในการคำนวณของคุณ

#include<stdio.h>
#include<math.h>
int main()
{
    int i,n,limit,numberOfDivisors=1;
    printf("Enter the number : ");
    scanf("%d",&n);
    limit=(int)sqrt((double)n);
    for(i=2;i<=limit;i++)
        if(n%i==0)
        {
            if(i!=n/i)
                numberOfDivisors+=2;
            else
                numberOfDivisors++;
        }
    printf("%d\n",numberOfDivisors);
    return 0;
}

แทนที่จะใช้ลูปด้านบนคุณสามารถใช้ลูปต่อไปนี้ซึ่งมีประสิทธิภาพมากกว่าเช่นนี้ทำให้ไม่ต้องค้นหาสแควร์รูทของจำนวน

for(i=2;i*i<=n;i++)
{
    ...
}

1

นี่คือฟังก์ชั่นที่ฉันเขียน มันซับซ้อนเวลาที่เลวร้ายที่สุดคือ O (sqrt (n)) เวลาที่ดีที่สุดในทางกลับกันคือ O (log (n)) มันช่วยให้คุณหารที่สำคัญทั้งหมดพร้อมกับจำนวนการเกิดขึ้นของมัน

public static List<Integer> divisors(n) {   
    ArrayList<Integer> aList = new ArrayList();
    int top_count = (int) Math.round(Math.sqrt(n));
    int new_n = n;

    for (int i = 2; i <= top_count; i++) {
        if (new_n == (new_n / i) * i) {
            aList.add(i);
            new_n = new_n / i;
            top_count = (int) Math.round(Math.sqrt(new_n));
            i = 1;
        }
    }
    aList.add(new_n);
    return aList;
}

ฉันไม่รู้ว่าฟังก์ชันนี้คำนวณอะไร แต่มันไม่ใช่รายชื่อตัวหารของ n
le_m

1

นี่เป็นวิธีพื้นฐานที่สุดในการคำนวณตัวหารจำนวน:

class PrintDivisors
{
    public static void main(String args[])
    {

    System.out.println("Enter the number");

    // Create Scanner object for taking input
    Scanner s=new Scanner(System.in);

    // Read an int
    int n=s.nextInt();

        // Loop from 1 to 'n'
        for(int i=1;i<=n;i++)
        {

            // If remainder is 0 when 'n' is divided by 'i',
            if(n%i==0)
            {
            System.out.print(i+", ");
            }
        }

    // Print [not necessary]    
    System.out.print("are divisors of "+n);

    }
}

1

@Kendall

ฉันทดสอบโค้ดของคุณและทำการปรับปรุงตอนนี้มันยิ่งเร็วขึ้น ฉันทดสอบด้วยรหัส @ هومنجاویدپورนี่ก็เร็วกว่ารหัสของเขาด้วย

long long int FindDivisors(long long int n) {
  long long int count = 0;
  long long int i, m = (long long int)sqrt(n);
  for(i = 1;i <= m;i++) {
    if(n % i == 0)
      count += 2;
  }
  if(n / m == m && n % m == 0)
    count--;
  return count;
}

0

นี่เป็นเพียงคำถามของการคำนวณจำนวน - พิจารณาปัจจัยทั้งหมดของตัวเลขหรือไม่ จากนั้นคุณสามารถตัดสินใจได้ว่าคุณต้องการชุดค่าผสมทั้งหมดอย่างน้อยหนึ่งปัจจัย

ดังนั้นหนึ่งอัลกอริทึมที่เป็นไปได้คือ:

factor(N)
    divisor = first_prime
    list_of_factors = { 1 }
    while (N > 1)
        while (N % divisor == 0)
            add divisor to list_of_factors
            N /= divisor
        divisor = next_prime
    return list_of_factors

จากนั้นขึ้นอยู่กับคุณที่จะรวมปัจจัยต่างๆเข้าด้วยกันเพื่อหาคำตอบที่เหลือ


0

นี่คือสิ่งที่ฉันคิดขึ้นกับคำตอบของจัสติน อาจต้องมีการปรับให้เหมาะสม

n=int(input())

a=[]
b=[]

def sieve(n):
    np = n + 1
    s = list(range(np)) 
    s[1] = 0
    sqrtn = int(n**0.5)
    for i in range(2, sqrtn + 1): 
        if s[i]:
            s[i*i: np: i] = [0] * len(range(i*i, np, i))
    return filter(None, s)

k=list(sieve(n))

for i in range(len(k)):
        if n%k[i]==0:
                a.append(k[i])

a.sort()

for i in range(len(a)):
        j=1
        while n%(a[i]**j)==0: 
                j=j+1
        b.append(j-1)

nod=1

for i in range(len(b)):
        nod=nod*(b[i]+1)

print('no.of divisors of {} = {}'.format(n,nod))

0

ฉันคิดว่านี่คือสิ่งที่คุณกำลังมองหาฉันทำสิ่งที่คุณขอ คัดลอกและวางไว้ใน Notepad บันทึกเป็น * .bat.Run.Enter Number.Multiply กระบวนการ 2 และนั่นคือจำนวนตัวหารฉันทำตามวัตถุประสงค์เพื่อให้ตัวหารเร็วขึ้น:

โปรดทราบว่า CMD สามารถรองรับค่าได้มากกว่า 999999999

@echo off

modecon:cols=100 lines=100

:start
title Enter the Number to Determine 
cls
echo Determine a number as a product of 2 numbers
echo.
echo Ex1 : C = A * B
echo Ex2 : 8 = 4 * 2
echo.
echo Max Number length is 9
echo.
echo If there is only 1 proces done  it
echo means the number is a prime number
echo.
echo Prime numbers take time to determine
echo Number not prime are determined fast
echo.

set /p number=Enter Number : 
if %number% GTR 999999999 goto start

echo.
set proces=0
set mindet=0
set procent=0
set B=%Number%

:Determining

set /a mindet=%mindet%+1

if %mindet% GTR %B% goto Results

set /a solution=%number% %%% %mindet%

if %solution% NEQ 0 goto Determining
if %solution% EQU 0 set /a proces=%proces%+1

set /a B=%number% / %mindet%

set /a procent=%mindet%*100/%B%

if %procent% EQU 100 set procent=%procent:~0,3%
if %procent% LSS 100 set procent=%procent:~0,2%
if %procent% LSS 10 set procent=%procent:~0,1%

title Progress : %procent% %%%



if %solution% EQU 0 echo %proces%. %mindet% * %B% = %number%
goto Determining

:Results

title %proces% Results Found
echo.
@pause
goto start

882161280 - นักหาร 1282 คน
dondon

0

ฉันเดาว่าอันนี้จะสะดวกและแม่นยำ

script.pyton

>>>factors=[ x for x in range (1,n+1) if n%x==0] print len(factors)


0

ลองทำตามบรรทัดเหล่านี้:

int divisors(int myNum) {
    int limit = myNum;
    int divisorCount = 0;
    if (x == 1) 
        return 1;
    for (int i = 1; i < limit; ++i) {
        if (myNum % i == 0) {
            limit = myNum / i;
            if (limit != i)
                divisorCount++;
            divisorCount++;
        }
    }
    return divisorCount;
}

-1

ฉันไม่ทราบวิธีที่มีประสิทธิภาพมากที่สุด แต่ฉันจะทำสิ่งต่อไปนี้:

  • สร้างตารางช่วงเวลาเพื่อค้นหาช่วงเวลาทั้งหมดที่น้อยกว่าหรือเท่ากับรากที่สองของจำนวน (โดยส่วนตัวแล้วฉันจะใช้ Sieve of Atkin)
  • นับจำนวนเฉพาะทั้งหมดน้อยกว่าหรือเท่ากับรากที่สองของจำนวนและคูณด้วยสอง หากสแควร์รูทของจำนวนนั้นเป็นจำนวนเต็มให้ลบออกจากตัวแปร count

ควรทำงาน \ o /

หากคุณต้องการฉันสามารถเขียนโค้ดในวันพรุ่งนี้เพื่อแสดงให้เห็น


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