จำนวนสตริงเมื่อแต่ละตัวละครจะต้องเกิดขึ้นแม้แต่ครั้ง


9

ฉันตีหัวกะโหลกที่ปัญหานี้มาระยะหนึ่งแล้วและมันเริ่มทำให้ฉันหงุดหงิดจริงๆ ปัญหาคือ:

ฉันมีชุดของตัวอักษร, A, B, และC Dฉันต้องบอกว่าสตริงสามารถสร้างจากตัวละครเหล่านี้ได้กี่วิธีเมื่อความยาวเป็นnและตัวละครแต่ละตัวจะต้องเกิดขึ้นซ้ำ ๆ

ตัวอย่างเช่นคำตอบสำหรับn = 2คือ 4:

AA
BB
CC
DD

คำตอบn = 4คือ 40 สตริงที่ใช้ได้บางส่วนคือ:

AAAA
AABB
CACA
DAAD
BCCB

ฉันติดอยู่กับตรรกะ ฉันรู้สึกว่าอาจมีวิธีแก้ปัญหา DP สำหรับสิ่งนี้ การบังคับให้ฉันผ่านสิ่งนี้ออกมาจากคำถาม: จำนวนโซลูชันเติบโตอย่างรวดเร็วเป็นจำนวนมหาศาล

ฉันได้ลองวาดไอเดียต่าง ๆ ลงบนกระดาษแล้วก็ไม่มีประโยชน์ ฉันต้องทิ้งความคิดเหล่านั้นเกือบทั้งหมดเนื่องจากความซับซ้อนของมันใหญ่เกินไป n = 10^4การแก้ปัญหาควรจะมีประสิทธิภาพสำหรับ

หนึ่งในความคิดของฉันคือไม่ต้องติดตามสายอักขระที่แท้จริง แต่เฉพาะว่าตัวละครแต่ละตัวนั้นปรากฏตัวหรือแม้แต่ช่วงเวลาคี่ ฉันไม่สามารถหาวิธีที่จะใช้ตรรกะนี้ได้

มีใครช่วยฉันบ้าง


3
คุณต้องการระบุสตริงหรือคำนวณจำนวนสตริงหรือไม่ หากคุณต้องการจำนวนสตริงเท่านั้นคุณสามารถใช้combinatoricsเพื่อคำนวณปริมาณได้โดยตรง

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

2
แน่นอนว่าคุณสามารถใช้ combinatorics สำหรับสตริงที่มีความยาว N ให้รับชุดทั้งหมดของ {AA, BB, CC, DD} สำหรับการรวมกันแต่ละครั้งจะได้รับการเปลี่ยนลำดับที่ไม่ซ้ำกัน จากนั้นรวมผลลัพธ์สำหรับการรวมกันเป็นชุดการเปลี่ยนลำดับที่ไม่ซ้ำกันหนึ่งชุด ฉันไม่แน่ใจว่าจะทำอย่างไรส่วนใหญ่เป็นเพราะข้อ จำกัด ที่ไม่ซ้ำกัน แต่ฉันแน่ใจว่ามีวิธี

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

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

คำตอบ:


5

ตั้งค่าf(n,d)เป็นฟังก์ชันที่ให้จำนวนการเรียงสับเปลี่ยนของความยาว (เท่ากัน) nโดยใช้dอักขระที่แตกต่าง (เช่นd=4ในกรณีของคุณ)

ชัดเจนf(0,d) = 1และf(n,1) = 1มีการจัดเรียงเพียงครั้งเดียวเมื่อคุณมีอักขระเพียงตัวเดียวหรือเว้นวรรคเป็นศูนย์

ตอนนี้ขั้นตอนการเหนี่ยวนำ:

ในการสร้างสตริงที่ถูกต้องโดยใช้dอักขระให้ใช้สตริงที่มีความยาวเท่ากันที่สั้นกว่าโดยใช้d-1อักขระและสร้างให้ยาวขึ้นโดยการเพิ่มอักขระใหม่หลายตัวเป็นทวีคูณ จำนวนการจัดเรียงเป็นchoose(n,n_newdigits)เพราะคุณสามารถเลือกn_newdigitสถานที่ที่มีความยาวสตริงทั้งหมดเพื่อให้มีตัวเลขใหม่และส่วนที่เหลือจะถูกเติมด้วยสตริงเดิมตามลำดับ

เมื่อต้องการใช้สิ่งนี้โดยใช้การเรียกซ้ำแบบไร้เดียงสาใน R ฉันได้:

f <- function(n,d)
{
  if(n==0) return(1)
  if(d==1) return(1)
  retval=0
  for (i in seq(from=0, to=n, by=2)) retval=retval+f(n-i,d-1)*choose(n,i)
  return(retval)
}

f(4,4)
# 40    

f(500,4)
# 1.339386e+300 takes about 10 secs

สำหรับประเภทของตัวเลขที่คุณสนใจฉันคิดว่ามันจะมีประสิทธิภาพมากกว่าในการจัดเก็บตัวเลขในอาร์เรย์สองมิติและวนซ้ำไปเรื่อย ๆ เพื่อเพิ่ม d แต่อาจขึ้นอยู่กับการเลือกภาษาของคุณ


4

คำตอบของ Miff นั้นสง่างามแน่นอน ตั้งแต่ฉันมีของฉันเกือบเสร็จแล้วต่อไปฉันให้มันอย่างไรก็ตาม สิ่งที่ดีคือฉันได้รับผลลัพธ์เดียวกันสำหรับ n = 500 :-)

ให้ d เป็นจำนวนตัวอักษรต่าง ๆ ที่อนุญาต d = 4 ในกรณีของคุณ

ให้ n เป็นความยาวของสตริงในท้ายที่สุดคุณจะดูค่าที่เท่ากันของ n

ให้คุณเป็นจำนวนอักขระที่ไม่ได้จับคู่ในสตริง

ให้ N (n, d, u) เป็นจำนวนสตริงที่มีความยาว n ซึ่งสร้างจากอักขระที่แตกต่างกัน d และมีอักขระที่ไม่ได้จับคู่ ให้ลองคำนวณ N

มีบางกรณีมุมที่สังเกตได้:

u> d หรือ u> n => N = 0

คุณ <0 => N = 0

n% 2! = u% 2 => N = 0

เมื่อก้าวจาก n เป็น n + 1 คุณต้องเพิ่มขึ้น 1 หรือลดลง 1 ดังนั้นเราจึงเรียกซ้ำตาม

N (n, d, u) = f (N (n-1, d, u-1), N (n-1, d, u + 1)

มีกี่วิธีที่จะช่วยลดคุณทีละคน อันนี้ง่ายเพราะเราต้องจับคู่หนึ่งในตัวละครที่ไม่มีคู่ซึ่งทำให้มันเป็นแค่คุณ ดังนั้นส่วนที่ 2 ของ f จะอ่าน (u + 1) * N (n-1, d, u + 1) โดยมีข้อแม้แน่นอนว่าเราต้องสังเกตว่า N = 0 ถ้าคุณ + 1> n-1 หรือ u 1> d

เมื่อเราเข้าใจสิ่งนี้มันเป็นเรื่องง่ายที่จะเห็นว่าส่วนแรกของ f คือ: เราสามารถเพิ่มคุณได้กี่วิธีเมื่อมีตัวละครที่ไม่มีคู่ u-1 เราต้องเลือกหนึ่งในอักขระ (k- (u-1)) ที่ถูกจับคู่

ดังนั้นเมื่อคำนึงถึงทุกมุมกรณีสูตรแบบเรียกซ้ำสำหรับ N คือ

N (n, d, u) = (d- (u-1)) * N (n-1, d, u-1) + (u + 1) * N (n-1, d, u + 1)

ฉันจะไม่อ่านในhttp://en.wikipedia.org/wiki/Concrete_Mathematicsวิธีแก้ปัญหาการเรียกซ้ำ

ฉันเขียนโค้ดจาวาแทน อีกเล็กน้อยเงอะงะมากขึ้นเช่นเดียวกับ Java ต่อไปเนื่องจากการใช้คำฟุ่มเฟื่อย แต่ฉันมีแรงจูงใจที่จะไม่ใช้การเรียกซ้ำเนื่องจากมันหยุดไปก่อนอย่างน้อยใน Java เมื่อกองซ้อนล้นที่ 500 หรือ 1,000 ระดับการซ้อน

ผลลัพธ์สำหรับ n = 500, d = 4 และ u = 0 คือ:

N (500, 4, 0) = 1339385758982834151185531311325002263201756014631917009304687985462938813906170153116497973519619822659493341146941433531483931607115392554498072196838958545795769042788035468026048125208904713757765805163872455056995809556627183222337328039422584942896842901774597806462162357229520744881314972303360

คำนวณใน 0.2 วินาทีเนื่องจากการจดจำผลลัพธ์ระดับกลาง N (40000,4,0) คำนวณในเวลาน้อยกว่า 5 วินาที รหัสด้วยที่นี่: http://ideone.com/KvB5Jv

import java.math.BigInteger;

public class EvenPairedString2 {
  private final int nChars;  // d above, number of different chars to use
  private int count = 0;
  private Map<Task,BigInteger> memo = new HashMap<>();

  public EvenPairedString2(int nChars) {
    this.nChars = nChars;
  }
  /*+******************************************************************/
  // encodes for a fixed d the task to compute N(strlen,d,unpaired).  
  private static class Task {
    public final int strlen;
    public final int unpaired;

    Task(int strlen, int unpaired) {
      this.strlen = strlen;
      this.unpaired = unpaired;
    }
    @Override
    public int hashCode() {
      return strlen*117 ^ unpaired;
    }
    @Override
    public boolean equals(Object other) {
      if (!(other instanceof Task)) {
        return false;
      }
      Task t2 = (Task)other;
      return strlen==t2.strlen && unpaired==t2.unpaired;
    }
    @Override
    public String toString() {
      return "("+strlen+","+unpaired+")";
    }
  }
  /*+******************************************************************/
  // return corner case or memorized result or null  
  private BigInteger getMemoed(Task t) {
    if (t.strlen==0 || t.unpaired<0 || t.unpaired>t.strlen || t.unpaired>nChars
        || t.strlen%2 != t.unpaired%2) {
      return BigInteger.valueOf(0);
    }

    if (t.strlen==1) {
      return BigInteger.valueOf(nChars);
    }
    return memo.get(t);
  }

  public int getCount() {
    return count;
  }

  public BigInteger computeNDeep(Task t) {
    List<Task> stack = new ArrayList<Task>();
    BigInteger result = null;
    stack.add(t);

    while (stack.size()>0) {
      count += 1;
      t = stack.remove(stack.size()-1);
      result = getMemoed(t);
      if (result!=null) {
        continue;
      }

      Task t1 = new Task(t.strlen-1, t.unpaired+1);
      BigInteger r1 = getMemoed(t1);
      Task t2 = new Task(t.strlen-1, t.unpaired-1);
      BigInteger r2 = getMemoed(t2);
      if (r1==null) {
        stack.add(t);
        stack.add(t1);
        if (r2==null) {
          stack.add(t2);
        }
        continue;
      }
      if (r2==null) {
        stack.add(t);
        stack.add(t2);
        continue;
      }
      result = compute(t1.unpaired, r1, nChars-t2.unpaired, r2);
      memo.put(t,  result);
    }
    return result;
  }
  private BigInteger compute(int u1, BigInteger r1, int u2, BigInteger r2) {
    r1 = r1.multiply(BigInteger.valueOf(u1));
    r2 = r2.multiply(BigInteger.valueOf(u2));
    return r1.add(r2);
  }
  public static void main(String[] argv) {
    int strlen = Integer.parseInt(argv[0]);
    int nChars = Integer.parseInt(argv[1]);

    EvenPairedString2 eps = new EvenPairedString2(nChars);

    BigInteger result = eps.computeNDeep(new Task(strlen, 0));
    System.out.printf("%d: N(%d, %d, 0) = %d%n", 
                      eps.getCount(), strlen, nChars, 
                      result); 
  }
}

2

ผมพยายามที่จะเกิดขึ้นกับวิธีแก้ปัญหา แต่ล้มเหลวและถามคำถามเดียวกันในMathematics.StackExchange ขอบคุณRus Mayนี่เป็นวิธีแก้ปัญหาใน Common LISP:

(defun solve (n)
  (if (evenp n)
      (/ (+ (expt 4 n) (* 4 (expt 2 n))) 8)
      0))

นี้มักจะกลับ 0 nสำหรับค่าคี่ สำหรับn = 500นี่คือผลลัพธ์ที่มีSBCL :

* (time (solve 500))

    Evaluation took:
      0.000 seconds of real time
      0.000000 seconds of total run time (0.000000 user, 0.000000 system)
      100.00% CPU
      51,100 processor cycles
      0 bytes consed

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