รู้ลำดับโดยเรียงตามลำดับ


18

บทนำ

สมมติว่าคุณและเพื่อนกำลังเล่นเกม เพื่อนของคุณคิดถึงลำดับของnบิตบางอย่างและงานของคุณคือการอนุมานลำดับโดยถามคำถาม อย่างไรก็ตามคำถามประเภทเดียวที่คุณได้รับอนุญาตให้ถามคือ "นานแค่ไหนที่การเรียงลำดับทั่วไปที่ยาวที่สุดของลำดับของคุณและS" ซึ่งSเป็นลำดับของบิตใด ๆ คำถามที่คุณต้องการน้อยลงเท่านั้น

งาน

งานของคุณคือการเขียนโปรแกรมหรือฟังก์ชั่นที่ใช้เป็น input เป็นจำนวนเต็มบวกnและลำดับไบนารีของความยาวR nลำดับอาจเป็นอาร์เรย์ของจำนวนเต็มสตริงหรือประเภทอื่น ๆ ที่คุณต้องการ Rโปรแกรมของคุณจะส่งออกลำดับ

โปรแกรมของคุณไม่ได้รับอนุญาตให้เข้าถึงลำดับRโดยตรง เพียงสิ่งที่จะได้รับอนุญาตให้ทำอย่างไรเพื่อRที่จะให้มันเป็น input เพื่อฟังก์ชั่นพร้อมกับลำดับไบนารีอื่นlen_lcs Sฟังก์ชั่นlen_lcs(R, S)ส่งกลับความยาวของ subsequence ทั่วไปที่ยาวที่สุดของและR Sซึ่งหมายความว่าลำดับที่ยาวที่สุดของบิตซึ่งเกิดขึ้นเป็น (ไม่จำเป็นต้องต่อเนื่องกัน) subsequence ทั้งในและR Sอินพุตlen_lcsที่อาจมีความยาวแตกต่างกัน โปรแกรมควรเรียกใช้ฟังก์ชันนี้Rและลำดับอื่น ๆ ตามจำนวนครั้งแล้วสร้างลำดับใหม่Rตามข้อมูลนั้น

ตัวอย่าง

พิจารณาปัจจัยการผลิตและn = 4 R = "1010"อันดับแรกเราอาจประเมินlen_lcs(R, "110")ซึ่งจะช่วยให้3เนื่องจาก"110"เป็น subsequence ทั่วไปที่ยาวที่สุดของและ"1010" "110"จากนั้นเราก็รู้ว่าRได้มาจากการ"110"แทรกตำแหน่งหนึ่งบิตในบางตำแหน่ง ต่อไปเราอาจลองlen_lcs(R, "0110")ซึ่งผลตอบแทน3ตั้งแต่ลำดับที่ยาวที่สุดร่วมกัน"110"และ"010"ดังนั้นจึง"0110"ไม่ถูกต้อง จากนั้นเราก็พยายามซึ่งผลตอบแทนlen_lcs(R, "1010") 4ตอนนี้เรารู้R == "1010"แล้วดังนั้นเราสามารถกลับลำดับนั้นเป็นเอาต์พุตที่ถูกต้อง ต้องมีการโทรถึง 3 len_lcsครั้ง

กฎและการให้คะแนน

ในแหล่งเก็บข้อมูลนี้คุณจะพบไฟล์ที่เรียกว่าsubsequence_data.txtมี 100 สุ่มลำดับไบนารีของความยาวระหว่าง 75 และ 124 พวกเขาถูกสร้างขึ้นโดยการสามลอยสุ่มระหว่าง 0 และ 1 ค่าเฉลี่ยของพวกเขาเป็นaแล้วพลิกaเหรียญ -biased nครั้ง คะแนนของคุณคือจำนวนการโทรโดยเฉลี่ยlen_lcsลำดับเหล่านี้คะแนนที่ต่ำกว่าจะดีกว่า การส่งของคุณควรบันทึกจำนวนการโทร ไม่มีการ จำกัด เวลายกเว้นว่าคุณควรเรียกใช้โปรแกรมของคุณในไฟล์ก่อนที่จะส่ง

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

นี่ไม่ใช่รหัสกอล์ฟดังนั้นคุณควรเขียนรหัสที่อ่านได้ Rosetta รหัสมีหน้าบน subsequence ทั่วไปที่ยาวที่สุด ; คุณอาจใช้สิ่งนั้นเพื่อนำไปใช้len_lcsในภาษาที่คุณเลือก


ไอเดียเด็ด ๆ ! มีแอปพลิเคชันใด ๆ หรือไม่
ข้อบกพร่อง

@flawr ฉันไม่รู้จักแอปพลิเคชันโดยตรง แนวคิดมาจากทฤษฎีความซับซ้อนของแบบสอบถามสาขาวิทยาศาสตร์คอมพิวเตอร์และมีแอปพลิเคชั่นมากมาย
Zgarb

ฉันคิดว่ามันจะดีที่จะมีความท้าทายเดียวกันอีกครั้ง แต่ที่คุณสามารถเข้าถึงแทนlcs len_lcs
ข้อบกพร่อง

@flawr ไม่ว่าจะน่าสนใจมากเนื่องจากผลตอบแทนlcs(R, "01"*2*n) R;) แต่ที่สามารถทำงานได้ถ้าโทรlcs(R, S)จะเพิ่มคะแนนโดยการlen(S)แทน 1, หรือสิ่งที่ต้องการ ...
Zgarb

1
ฉันชอบที่จะเห็นคำตอบอื่น ๆ = S
ข้อบกพร่อง

คำตอบ:


10

Java, 99.04 98.46 97.66 lcs () การโทร

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

Exaple: 00101สายของเราเป็นผู้ที่จะสร้างขึ้นใหม่มี ครั้งแรกที่เราหาวิธีหลายศูนย์มีโดยการเปรียบเทียบ (ที่นี่เปรียบเทียบ = LCS กับสตริงเดิมคำนวณ) 00000โดยศูนย์สตริงเท่านั้น จากนั้นเราไปในแต่ละตำแหน่งพลิก0ไปที่ a 1และตรวจสอบว่าตอนนี้เรามีซับสตริงที่ยาวกว่าปกติหรือไม่ ถ้าใช่ยอมรับและไปที่ตำแหน่งถัดไปถ้าไม่ให้พลิกกระแส1กลับไปที่ a 0และไปที่ตำแหน่งถัดไป:

For our example of "00101" we get following steps:
input  lcs  prev.'best'
00000  3    0           //number of zeros
̲10000  3    3           //reject
0̲1000  3    3           //reject
00̲100  4    3           //accept
001̲10  4    4           //reject
0010̲1  5    4           //accept

การเพิ่มประสิทธิภาพ

นี่เป็นเพียงการนำ "ไร้เดียงสา" มาใช้บางทีมันอาจเป็นไปได้ที่จะหา alogrithm ที่ซับซ้อนยิ่งขึ้นตรวจสอบหลาย ๆ ตำแหน่งพร้อมกัน แต่ผมไม่แน่ใจว่าถ้ามีจริงๆเป็นหนึ่งที่ดีกว่า (เช่นอยู่บนพื้นฐานของการคำนวณของบิตเท่าเทียมกันคล้ายกับรหัส Hamming ที่) ในขณะที่คุณสามารถเสมอเพียงการประเมินความยาวของสตริงย่อยทั่วไป

สำหรับตัวเลขหนึ่งบรรทัดที่กำหนดอัลกอริทึมนี้ต้อง#ofDigitsUntilTheLastOccurenceOf1 + 1ตรวจสอบอย่างแน่นอน (ย่อท้ายหนึ่งถ้าหลักสุดท้ายคือ1.)

แก้ไข: การปรับให้เหมาะสมเล็ก ๆ น้อย ๆ : ถ้าเราเพิ่งตรวจสอบตัวเลขสุดท้ายที่ 2 และเรายังต้องใส่ a 1เรารู้ว่ามันจะต้องอยู่ในตำแหน่งสุดท้ายและสามารถละเว้นการตรวจสอบที่เกี่ยวข้อง

แก้ไข 2: ฉันเพิ่งสังเกตเห็นว่าคุณสามารถใช้ความคิดข้างต้นกับkคนสุดท้าย

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

Runtime

ขีด จำกัด O(#NumberOfBits)บนคือ

รหัสเต็ม

นี่คือรหัสเต็ม:

package jcodegolf;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

// http://codegolf.stackexchange.com/questions/69799/know-a-sequence-by-its-subsequences

public class SequenceReconstructor { 
    public static int counter = 0;
    public static int lcs(String a, String b) { //stolen from http://rosettacode.org/wiki/Longest_common_subsequence#Java
        int[][] lengths = new int[a.length()+1][b.length()+1];

        // row 0 and column 0 are initialized to 0 already

        for (int i = 0; i < a.length(); i++)
            for (int j = 0; j < b.length(); j++)
                if (a.charAt(i) == b.charAt(j))
                    lengths[i+1][j+1] = lengths[i][j] + 1;
                else
                    lengths[i+1][j+1] =
                        Math.max(lengths[i+1][j], lengths[i][j+1]);

        // read the substring out from the matrix
        StringBuffer sb = new StringBuffer();
        for (int x = a.length(), y = b.length();
             x != 0 && y != 0; ) {
            if (lengths[x][y] == lengths[x-1][y])
                x--;
            else if (lengths[x][y] == lengths[x][y-1])
                y--;
            else {
                assert a.charAt(x-1) == b.charAt(y-1);
                sb.append(a.charAt(x-1));
                x--;
                y--;
            }
        }

        counter ++;
        return sb.reverse().toString().length();
    }


    public static String reconstruct(String secretLine, int lineLength){
        int current_lcs = 0; 
        int previous_lcs = 0;
        char [] myGuess = new char[lineLength];
        for (int k=0; k<lineLength; k++){
            myGuess[k] = '0';
        }

        //find the number of zeros:
        int numberOfZeros = lcs(secretLine, String.valueOf(myGuess));
        current_lcs = numberOfZeros;
        previous_lcs = numberOfZeros;

        if(current_lcs == lineLength){ //were done
            return String.valueOf(myGuess);
        }


        int numberOfOnes = lineLength - numberOfZeros;
        //try to greedily insert ones at the positions where they maximize the common substring length
        int onesCounter = 0;
        for(int n=0; n < lineLength && onesCounter < numberOfOnes; n++){

            myGuess[n] = '1';
            current_lcs = lcs(secretLine, String.valueOf(myGuess));

            if(current_lcs > previous_lcs){ //accept

                previous_lcs = current_lcs;
                onesCounter ++;

            } else { // do not accept
                myGuess[n]='0';     
            }

            if(n == lineLength-(numberOfOnes-onesCounter)-1 && onesCounter < numberOfOnes){ //lets test if we have as many locations left as we have ones to insert
                                                                // then we know that the rest are ones
                for(int k=n+1;k<lineLength;k++){
                    myGuess[k] = '1';
                }
                break;
            }

        }

        return String.valueOf(myGuess);
    }

    public static void main(String[] args) {
        try {

            //read the file
            BufferedReader br;

            br = new BufferedReader(new FileReader("PATH/TO/YOUR/FILE/LOCATION/subsequence_data.txt"));

            String line;

            //iterate over each line
            while ( (line = br.readLine()) != null){

                String r = reconstruct(line, line.length());
                System.out.println(line);     //print original line
                System.out.println(r);        //print current line
                System.out.println(counter/100.0);  //print current number of calls
                if (! line.equals(r)){
                    System.out.println("SOMETHING WENT HORRIBLY WRONG!!!");
                    System.exit(1);
                }

            }


        } catch(Exception e){
            e.printStackTrace();;
        }

    }

}

1
เนื่องจากคุณได้รับการโทรน้อยลงเมื่อมีการต่อท้าย 1s ดูเหมือนว่าคุณสามารถทำได้ดีกว่าโดยเฉลี่ยถ้าหลังจากการเดาครั้งแรกบอกว่าคุณมี 0s มากกว่า 1 วินาทีคุณเปลี่ยนเป็นการล่าสัตว์สำหรับตำแหน่ง 0 มากกว่า 1 ตำแหน่ง คุณสามารถทำได้หลายครั้ง
ชำนาญวิชาประวัติศาสตร์

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