นับอาร์เรย์ของช่วงเวลา


11

periodของสตริงเป็นที่ไม่ใช่ศูนย์การเปลี่ยนแปลงที่สั้นที่สุดเพื่อให้สตริงตรงกับตัวเองโดยไม่สนใจชิ้นส่วนใด ๆ ที่แขวนอยู่ ดังนั้นสำหรับตัวอย่างเช่นมีระยะเวลาabcabcab 3โดยการประชุมเราบอกว่าถ้าไม่มีการเปลี่ยนแปลงเช่นนั้นสตริงมีระยะเวลาเท่ากับความยาวของมัน ดังนั้นระยะเวลาของการabcdeเป็น5ระยะเวลาของการมี a1

ในแง่ที่เป็นทางการมากขึ้นช่วงเวลาของสตริงSคือขั้นต่ำi > 0ดังนั้นS[1,n-i] == S[i+1,n](การจัดทำดัชนีจาก1)

สำหรับสตริง S ของกำลังสองที่มีความยาวเราจะคำนวณระยะเวลาของคำนำหน้าทั้งหมดของกำลังสองยาว S = abcabcabตัวอย่างเช่นพิจารณา ช่วงเวลาที่เราจะคำนวณคือ:

'a', 1
'ab', 2
'abca', 3
'abcabcab', 3

[1, 2, 3, 3]เราจะอยู่ในความเป็นจริงเพียงแค่การส่งออกอาร์เรย์ของระยะเวลาที่เป็น

สำหรับพลังบวกให้สองพิจารณาทั้งหมดสตริงไบนารีที่เป็นไปได้n Sโปรดจำไว้ว่าสตริงไบนารีเป็นเพียงสตริงของ1s และ0s ดังนั้นจึงมี2^nสตริงดังกล่าว (นั่นคือ2กำลังn) สำหรับแต่ละคนเราสามารถคำนวณช่วงเวลานี้ได้

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

คำตอบสำหรับn = 1, 2, 4, 8, 16, 32, 64, 128คือ:

1, 2, 6, 32, 320, 6025, 216854, 15128807

ชุดของช่วงเวลาที่แตกต่างกันสำหรับn = 4:

1, 1, 1
1, 1, 3
1, 1, 4
1, 2, 2
1, 2, 3
1, 2, 4

คะแนน

ฉันจะเรียกใช้รหัสของคุณบนคอมพิวเตอร์ที่ใช้ Ubuntu เป็นเวลา 10 นาที คะแนนของคุณใหญ่ที่สุดnซึ่งรหัสของคุณสิ้นสุดลงในเวลานั้น ในกรณีที่เสมอกันคำตอบนั้นจะเป็นการnชนะที่เร็วที่สุด ในกรณีที่มีการเสมอกันภายใน 1 วินาทีในการจับเวลาคำตอบแรกที่โพสต์ชนะ

ภาษาและห้องสมุด

คุณสามารถใช้ภาษาและไลบรารีใด ๆ ที่คุณต้องการ โปรดระบุคำอธิบายโดยละเอียดเกี่ยวกับวิธีการเรียกใช้ / รวบรวมรหัสของคุณใน Linux หากเป็นไปได้ทั้งหมด”

รหัสของคุณควรคำนวณคำตอบจริง ๆ ไม่ใช่เพียงแค่เอาท์พุทที่คำนวณไว้ล่วงหน้า

รายการชั้นนำ

  • 2 นาทีและ 21 วินาทีสำหรับn = 128ในC #โดย Peter Taylor
  • 9 วินาทีสำหรับn = 32ในRustโดย isaacg

นี่ทำให้ฉันปวดหัว
เฮนรี่

1
ความท้าทายเป็นสิ่งที่น่าสนใจ แต่ฉันก็ยังไม่เห็นเกณฑ์วัตถุประสงค์ที่คุณใช้เพื่อแยกความแตกต่างระหว่างคำตอบ"precomputed"และ"คำนวณจริง" ตัวอย่างเช่นถ้าคุณไม่เข้าใจว่าโค้ดของฉันทำงานอย่างไร แต่มันให้คำตอบที่ถูกต้องสำหรับเรื่องใหญ่nๆ คุณจะยอมรับมันไหม? มันไม่ได้ถูกกำหนดอย่างชัดเจนว่าเป็นจุดที่อยู่ระหว่างการเข้ารหัสและการคำนวณจริง


1
@ThePirateBay codegolf.meta.stackexchange.com/a/1063/9206 มันเป็นกฎมาตรฐาน

2
@Cowsquack ทั้งหมด abcabแต่ตัวอักษรสามตัวแรกของสตริง ทั้งหมด แต่สุดท้าย 3 abcabตัวอักษรเป็น การจับคู่เหล่านี้และการลบตัวอักษรจำนวนน้อยไม่ตรงกัน
isaacg

คำตอบ:


9

C #, n = 128 ในประมาณ 2:40

using System;
using System.Collections.Generic;
using System.Linq;

namespace Sandbox
{
    class PPCG137436
    {
        public static void Main(string[] args)
        {
            if (args.Length == 0) args = new string[] { "1", "2", "4", "8", "16", "32", "64", "128" };

            foreach (string arg in args)
            {
                Console.WriteLine(Count(new int[(int)(0.5 + Math.Log(int.Parse(arg)) / Math.Log(2))], 0));
            }
        }

        static int Count(int[] periods, int idx)
        {
            if (idx == periods.Length)
            {
                //Console.WriteLine(string.Join(", ", periods));
                return 1;
            }

            int count = 0;
            int p = idx == 0 ? 1 : periods[idx - 1];
            for (int q = p; q <= 1 << (idx + 1); q++)
            {
                periods[idx] = q;
                if (q == p || q > 1 << idx || p + q - Gcd(p, q) > 1 << idx && UnificationPasses(periods, idx, q)) count += Count(periods, idx + 1);
            }

            return count;
        }

        private static int Gcd(int a, int b)
        {
            while (a > 0) { int tmp = a; a = b % a; b = tmp; }
            return b;
        }

        private static bool UnificationPasses(int[] periods, int idx, int q)
        {
            UnionSet union = new UnionSet(1 << idx);
            for (int i = 0; i <= idx; i++)
            {
                for (int j = 0; j + periods[i] < Math.Min(2 << i, 1 << idx); j++) union.Unify(j, j + periods[i]);
            }

            IDictionary<int, long> rev = new Dictionary<int, long>();
            for (int k = 0; k < 1 << idx; k++) rev[union.Find(k)] = 0L;
            for (int k = 0; k < 1 << idx; k++) rev[union.Find(k)] |= 1L << k;

            long zeroes = rev[union.Find(0)]; // wlog the value at position 0 is 0

            ISet<int> onesIndex = new HashSet<int>();

            // This can be seen as the special case of the next loop where j == -1.
            for (int i = 0; i < idx; i++)
            {
                if (periods[i] == 2 << i) onesIndex.Add((2 << i) - 1);
            }
            for (int j = 0; j < idx - 1 && periods[j] == 2 << j; j++)
            {
                for (int i = j + 1; i < idx; i++)
                {
                    if (periods[i] == 2 << i)
                    {
                        for (int k = (1 << j) + 1; k <= 2 << j; k++) onesIndex.Add((2 << i) - k);
                    }
                }
            }

            for (int i = 1; i < idx; i++)
            {
                if (periods[i] == 1) continue;

                int d = (2 << i) - periods[i];
                long dmask = (1L << d) - 1;
                if (((zeroes >> 1) & (zeroes >> periods[i]) & dmask) == dmask) onesIndex.Add(periods[i] - 1);
            }

            long ones = 0L;
            foreach (var key in onesIndex) ones |= rev[union.Find(key)];

            if ((zeroes & ones) != 0) return false; // Definite contradiction!

            rev.Remove(union.Find(0));
            foreach (var key in onesIndex) rev.Remove(key);

            long[] masks = System.Linq.Enumerable.ToArray(rev.Values);

            int numFilteredMasks = 0;
            long set = 0;
            long M = 0;
            for (int i = 1; i <= idx; i++)
            {
                if (periods[i - 1] == 1) continue;

                // Sort the relevant masks to the start
                if (i == idx) numFilteredMasks = masks.Length; // Minor optimisation: skip the filter because we know we need all the masks
                long filter = (1L << (1 << i)) - 1;
                for (int j = numFilteredMasks; j < masks.Length; j++)
                {
                    if ((masks[j] & filter) != 0)
                    {
                        var tmp = masks[j];
                        masks[j] = masks[numFilteredMasks];
                        masks[numFilteredMasks++] = tmp;
                    }
                }

                // Search for a successful assignment, using the information from the previous search to skip a few initial values in this one.
                set |= (1L << numFilteredMasks) - 1 - M;
                M = (1L << numFilteredMasks) - 1;
                while (true)
                {
                    if (TestAssignment(periods, i, ones, masks, set)) break;
                    if (set == 0) return false; // No suitable assignment found

                    // Gosper's hack with variant to reduce the number of bits on overflow
                    long c = set & -set;
                    long r = set + c;
                    set = (((r ^ set) >> 2) / c) | (r & M);
                }
            }

            return true;
        }

        private static bool TestAssignment(int[] periods, int idx, long ones, long[] masks, long assignment)
        {
            for (int j = 0; j < masks.Length; j++, assignment >>= 1) ones |= masks[j] & -(assignment & 1);
            for (int i = idx - 1; i > 0; i--) // i == 0 is already handled in the unification process.
            {
                if (Period(ones, 2 << i, periods[i - 1]) < periods[i]) return false;
            }

            return true;
        }

        private static int Period(long arr, int n, int min)
        {
            for (int p = min; p <= n; p++)
            {
                // If the bottom n bits have period p then the bottom (n-p) bits equal the bottom (n-p) bits of the integer shifted right p
                long mask = (1L << (n - p)) - 1L;
                if ((arr & mask) == ((arr >> p) & mask)) return p;
            }

            throw new Exception("Unreachable");
        }

        class UnionSet
        {
            private int[] _Lookup;

            public UnionSet(int size)
            {
                _Lookup = new int[size];
                for (int k = 0; k < size; k++) _Lookup[k] = k;
            }

            public int Find(int key)
            {
                var l = _Lookup[key];
                if (l != key) _Lookup[key] = l = Find(l);
                return l;
            }

            public void Unify(int key1, int key2)
            {
                int root1 = Find(key1);
                int root2 = Find(key2);

                if (root1 < root2) _Lookup[root2] = root1;
                else _Lookup[root1] = root2;
            }
        }
    }
}

การขยายไปยัง n = 256 จะต้องเปลี่ยนไปBigIntegerใช้มาสก์ซึ่งอาจฆ่าประสิทธิภาพมากเกินไปสำหรับ n = 128 ที่จะผ่านไปโดยไม่มีแนวคิดใหม่ให้อยู่คนเดียว n = 256

ภายใต้ Linux, รวบรวมและดำเนินการกับmono-cscmono

คำอธิบายพื้นฐาน

ฉันจะไม่แยกส่วนทีละบรรทัดเพียงภาพรวมของแนวคิด

ตามกฎของหัวแม่มือฉันยินดีที่จะย้ำผ่านตามลำดับของ 2 50องค์ประกอบในโปรแกรม combinatoric เดรัจฉานบังคับ ในการไปถึง n = 128 ดังนั้นจึงจำเป็นต้องใช้วิธีการที่ไม่ได้วิเคราะห์ทุก bitstring ดังนั้นแทนที่จะใช้การส่งต่อจากสตริงบิตไปยังลำดับรอบระยะเวลาฉันทำงานย้อนหลัง: กำหนดลำดับของรอบเวลามีบิตสตริงที่รู้หรือไม่ สำหรับ n = 2 xมีขอบเขตบนที่ง่ายของ 2 x (x + 1) / 2 จุดลำดับ (vs 2 2 x bitstrings)

บางข้อโต้แย้งใช้คำศัพท์เฉพาะของสตริง :

ให้pและจะมีสองช่วงเวลาของสตริงของความยาวq nหากp + q ≤ n + gcd(p, q)แล้วgcd(p, q)ยังเป็นช่วงเวลาของการสตริง

WLOG ผมจะสมมติว่าทุก bitstrings 0ภายใต้การพิจารณาการเริ่มต้นกับ

เมื่อกำหนดลำดับของรอบระยะเวลาซึ่งเป็นช่วงเวลาของส่วนนำหน้าของความยาว 2 i ( เสมอ) จะมีข้อสังเกตง่ายๆเกี่ยวกับค่าที่เป็นไปได้ของ:[p1 p2 ... pk]pip0 = 1pk+1

  • pk+1 ≥ pkตั้งแต่สมัยของสตริงยังเป็นช่วงเวลาของการคำนำหน้าใดSS

  • pk+1 = pk เป็นส่วนขยายที่เป็นไปได้เสมอ: เพียงทำซ้ำสตริงดั้งเดิมดั้งเดิมสองเท่าของอักขระจำนวนมาก

  • 2k < pk+1 ≤ 2k+1เป็นนามสกุลที่เป็นไปได้เสมอ มันพอเพียงที่จะแสดงสิ่งนี้เพราะสตริงความยาว aperiodic สามารถขยายเป็นสตริงความยาว aperiodic ได้โดยการเพิ่มตัวอักษรใด ๆ ที่ไม่ใช่ตัวอักษรตัวแรกpk+1 = 2k+1LL+1

    ใช้สตริงSxที่มีความยาว 2 kซึ่งมีระยะเวลาและพิจารณาสตริงที่มีความยาว 2 + 1 k เห็นได้ชัดว่ามีระยะเวลา 2 k +1 สมมติว่าช่วงเวลานั้นเล็กลงpkSxySSxySq

    จากนั้นตามช่วงเวลาบทแทรกก็เป็นช่วงเวลาและเนื่องจากตัวหารที่ยิ่งใหญ่ที่สุดมีค่าน้อยกว่าหรือเท่ากับอาร์กิวเมนต์และเป็นช่วงเวลาที่เล็กที่สุดเราจึงจำเป็นต้องเป็นปัจจัยที่เหมาะสมของ 2 k +1 เนื่องจากความฉลาดของมันไม่สามารถเป็นที่ 2 เรามี2k+1 + q ≤ 2k+1+1 ≤ 2k+1 + gcd(2k+1, q)gcd(2k+1, q)SxySqqq ≤ (2k+1)/3

    ตอนนี้ตั้งแต่เป็นช่วงเวลาของมันจะต้องเป็นช่วงเวลาของ แต่ระยะเวลาของการมี เรามีสองกรณี:q ≤ 2kSxySSxSxpk

    1. gcd(pk, q) = pkหรือแบ่งให้เท่ากันทั้งหมดpkq
    2. pk + q > 2k + gcd(pk, q) ดังนั้นการแทรกช่วงเวลาไม่ได้บังคับช่วงเวลาที่สั้นลง

    พิจารณากรณีแรกก่อน , ขัดแย้งกับความหมายของระยะเวลาของ ดังนั้นเราจึงจะถูกบังคับให้ข้อสรุปที่เป็นปัจจัยของpk > 2k + gcd(pk, q) - q ≥ 2k+1 - q ≥ 2k+1 - (2k+1)/3 ≥ 2qpkSxpkq

    แต่เนื่องจากqเป็นช่วงเวลาSxและเป็นช่วงเวลาคำนำหน้าของความยาวจึงเป็นเพียงสำเนาของคำนำหน้าความยาวดังนั้นเราจึงเห็นว่าเป็นช่วงเวลาด้วยpkSxqq/pkpkpkSxyS

    ดังนั้นช่วงเวลาSxySคือหรือ 2 k +1 แต่เรามีสองทางเลือก! อย่างน้อยหนึ่งตัวเลือกจะให้ช่วงเวลาดังนั้นอย่างน้อยหนึ่งตัวเลือกจะให้ช่วงเวลา 2 k +1 QEDpkyypk

  • บทแทรกช่วงเวลาช่วยให้เราสามารถปฏิเสธส่วนขยายที่เหลืออยู่บางส่วนได้

  • ส่วนขยายใด ๆ ที่ไม่ผ่านการยอมรับอย่างรวดเร็วหรือการทดสอบแบบปฏิเสธด่วนจะต้องทำการทดสอบอย่างสร้างสรรค์

การสร้าง bitstring ที่กำหนดลำดับของช่วงเวลานั้นเป็นปัญหาที่น่าพอใจ แต่ก็มีโครงสร้างจำนวนมาก มีข้อ จำกัด ด้านความเท่าเทียมกันอย่างง่าย ๆ ที่บอกเป็นนัยในแต่ละช่วงเวลาของคำนำหน้าดังนั้นฉันจึงใช้โครงสร้างข้อมูลแบบชุดยูเนี่ยนเพื่อรวมบิตเป็นกลุ่มอิสระ นี่เพียงพอที่จะจัดการกับ n = 64 แต่สำหรับ n = 128 มันจำเป็นต้องดำเนินการต่อไป ฉันใช้อาร์กิวเมนต์ที่มีประโยชน์สองบรรทัด:2k - pk

  1. ถ้าคำนำหน้าของความยาวMเป็นและคำนำหน้าของความยาวที่มีระยะเวลาแล้วคำนำหน้าของความยาวต้องลงท้ายด้วย นี่เป็นสิ่งที่ทรงพลังที่สุดในกรณีที่จะมีกลุ่มอิสระที่สะดวกที่สุด01M-1L > MLL1M
  2. ถ้าคำนำหน้าของความยาวMเป็นและคำนำหน้าของความยาวที่มีระยะเวลาที่มีและสิ้นสุดในนั้นจะต้องอยู่ในความเป็นจริงในตอนท้าย นี่เป็นสิ่งที่ทรงพลังที่สุดในสิ่งที่ตรงกันข้ามมากที่สุดเมื่อลำดับรอบระยะเวลาเริ่มต้นด้วยจำนวนมาก0ML > ML - dd < M0d10d

หากเราไม่ได้รับความขัดแย้งในทันทีโดยบังคับให้คลัสเตอร์ที่มีบิตแรก (สันนิษฐานว่าเป็นศูนย์) เป็นหนึ่งแล้วเราจะเดรัจฉานแรง (กับ micro-optimisations บาง) มากกว่าค่าที่เป็นไปได้สำหรับกลุ่มไม่ได้บังคับ ทราบว่าสั่งซื้อจากมากไปน้อยจำนวนคนเพราะถ้าiTHบิตเป็นหนึ่งจากนั้นช่วงเวลาที่ไม่สามารถiและเราอยากจะหลีกเลี่ยงช่วงเวลาที่สั้นกว่าคนที่มีการบังคับใช้แล้วโดยการจัดกลุ่ม การลงไปเพิ่มโอกาสในการค้นหาการบ้านที่ถูกต้อง แต่เนิ่นๆ


นี่คือความสำเร็จที่ยอดเยี่ยมจริงๆ! ฉันประทับใจมาก.

@ Lembik ฉันได้ง่ายขึ้นและเพิ่มประสิทธิภาพรหัสและรันไทม์ลดลงสำหรับ n = 128 ประมาณหนึ่งในสาม
ปีเตอร์เทย์เลอร์

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

7

สนิม, 32, 10s 11s 29sบนแล็ปท็อปของฉัน

เรียกมันว่า bitsize เป็นอาร์กิวเมนต์บรรทัดคำสั่ง

เทคนิคที่ชาญฉลาด: แทน bitstrings โดยตรงเป็นตัวเลขใช้ bittwiddling เพื่อตรวจสอบรอบ ค้นหาเฉพาะครึ่งแรกของบิตสเตรตซึ่งเริ่มต้นด้วย 0 เนื่องจากอาเรย์ของช่วงเวลาของบิตสตริงและค่าผกผัน (0s ที่สลับเป็น 1s) จะเหมือนกัน หากความเป็นไปได้ทั้งหมดสำหรับตำแหน่งสุดท้ายได้เกิดขึ้นแล้วฉันจะไม่ค้นหา

สิ่งที่ฉลาดกว่านี้:

ในการขจัดความซ้ำซ้อนของแต่ละบล็อก (สตริงที่ครึ่งแรกของบิตเหมือนกัน) ฉันใช้ bitvector ซึ่งเร็วกว่า hashset มากเนื่องจากความยาวรอบสุดท้ายไม่จำเป็นต้อง hashing

นอกจากนี้ฉันข้ามขั้นตอนแรกของการตรวจสอบรอบเนื่องจากฉันรู้ว่ารอบสุดท้ายไม่ควรสั้นกว่ารอบที่สองถึงครั้งสุดท้าย

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

//extern crate cpuprofiler;
//use cpuprofiler::PROFILER;

extern crate bit_vec;
use bit_vec::BitVec;

use std::collections::HashSet;

fn cycle_len(num: u32, mask: u32, skip_steps: usize) -> usize {
    let mut left = num >> skip_steps;
    let mut mask = mask >> skip_steps;
    let mut steps = skip_steps;
    loop {
        left >>= 1;
        if left == (num & mask) {
            return steps;
        }
        mask >>= 1;
        steps += 1;
    }
}

fn all_cycles(size_log: usize) -> HashSet<Vec<usize>> {
    let mut set = HashSet::new();
    if size_log == 0 {
        set.insert(vec![]);
        return set;
    } else if size_log == 1 {
        set.insert(vec![0]);
        set.insert(vec![1]);
        return set;
    }
    let size: usize = 1 << size_log;
    let half_size: usize = 1 << size_log - 1;
    let shift_and_mask: Vec<(usize, u32)> = (1..size_log)
        .map(|subsize_log| {
            let subsize = 1 << subsize_log;
            (size - subsize, (1 << (subsize - 1)) - 1)
        })
        .collect();
    let size_mask = (1 << (size - 1)) - 1;
    for block in 0..(1 << (half_size - 1)) as u32 {
        let start: u32 = block << half_size;
        if block % 1024 == 0 {
            eprintln!(
                "{} ({:.2}%): {}",
                start,
                start as f64 / (1u64 << size - 1) as f64 * 100f64,
                set.len()
            );
        }
        let leader = {
            let mut cycles = Vec::new();
            for &(shift, mask) in &shift_and_mask {
                let subnum = start >> shift;
                cycles.push(cycle_len(subnum, mask, 0));
            }
            cycles
        };
        let &end = leader.last().unwrap();
        if (end..size).all(|count| {
            let mut new = leader.clone();
            new.push(count);
            set.contains(&new)
        })
        {
            continue;
        }
        let mut subset = BitVec::from_elem(size, false);
        for num in start..start + (1 << half_size) {
            subset.set(cycle_len(num, size_mask, end), true);
        }
        for (unique_cycle_len, _) in subset.into_iter().enumerate().filter(|x| x.1) {
            let mut new_l = leader.clone();
            new_l.push(unique_cycle_len);
            set.insert(new_l);
        }
    }
    set
}

fn main() {
    let size: f32 = std::env::args().nth(1).unwrap().parse().unwrap();
    let size_log = size.log2() as usize;
    //PROFILER.lock().unwrap().start("./my-prof.profile").unwrap();
    let cycles = all_cycles(size_log);
    //PROFILER.lock().unwrap().stop().unwrap();
    println!(
        "Number of distinct arrays of periods of bitstrings of length {} is {}",
        1 << size_log,
        cycles.len()
    );
}

ใส่bit-vec = "0.4.4"ใน Cargo.toml ของคุณ

หากคุณต้องการที่จะทำงานนี้โคลนนี้: github.com/isaacg1/cycle แล้วCargo build --releaseที่จะสร้างแล้วCargo run --release 32วิ่ง


ดูเหมือนว่า eprintln ต้องการเวอร์ชั่นของสนิมหลังจาก 0.16.0 มันใช้งานได้ถ้าฉันเปลี่ยนเป็น println

คำตอบนี้น่าประทับใจมาก timeให้เวลาผู้ใช้ถึง 27 วินาทีบนแล็ปท็อปของฉัน

@ Lembik ทำไมคุณถึงเป็นสนิมรุ่นเก่าแบบนี้? สนิม 1.0 ออกมาเมื่อหลายปีก่อน
isaacg

Typo :) ฉันหมายถึง 1.16.0 blog.rust-lang.org/2017/03/16/Rust-1.16.html

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

4

สนิม 16

use std::collections::HashSet;
use std::io;

fn main() {
	print!("Enter a pow of two:");
	let mut input_text = String::new();
    io::stdin()
        .read_line(&mut input_text)
        .expect("failed to read from stdin");

    let n_as_string = input_text.trim();
	match n_as_string.parse::<usize>() {
		Ok(n) => {
			let log2 = (n as f64).log(2_f64) as usize;
			if n != 1 << log2 {
				panic!("{} is not a power of two", n);
			}
			let count = compute_count_array(log2, n);
			println!("n = {} -> count = {}", n, count);
		}
		Err(_) => { panic!("{} is not a number", n_as_string); }
	}
}

fn compute_count_array(log2:usize, n: usize) -> usize {
	let mut z = HashSet::new();

	let mut s:Vec<bool> = vec!(false; n);
	loop {
		let mut y:Vec<usize> = vec!();
		for j in 0..log2+1 {
			let p = find_period(&s[0..1<<j]);
			y.push(p);
		}		
		z.insert(y);
		if !next(&mut s) {
			break;
		}
	}
	z.len()
}

#[inline]
fn find_period(s: &[bool]) -> usize {
	let n=s.len();
	let mut j=1;
	while j<n {
		if s[0..n-j] == s[j..n] {
			return j;
		}
		j+=1;
    }
	n
}	

#[inline]
fn next(s:&mut Vec<bool>) -> bool {
	if s[0] {
		s[0] = false;
		for i in 1..s.len() {
			if s[i] {
				s[i] = false;
			} else {
				s[i] = true;
				return true;
			}
		}
		return false
	} else {
		s[0] = true;
	}
	true
}

ลองออนไลน์!

รวบรวม: rustc -O <name>.rs

สตริงถูกนำมาใช้เป็นเวกเตอร์ Bool

  • nextย้ำฟังก์ชั่นการรวมนั้น

  • The find_periodใช้ a Bool slice และคืนค่างวด

  • compute_count_arrayไม่ทำงานสำหรับแต่ละ "พลังของทั้งสอง" subsequence ของการรวมกันของแต่ละ bools

ในทางทฤษฎีไม่ล้นคาดว่าจน2^nเกินค่าสูงสุด u64 n > 64คือ ขีด จำกัด นี้อาจแผ่ออกไปพร้อมกับการทดสอบราคาแพงใน s = [จริงจริง ... ... จริง]

ข่าวร้ายคือมันส่งคืน 317 สำหรับ n = 16 แต่ฉันไม่รู้ว่าทำไม ฉันไม่รู้เหมือนกันว่าจะทำให้เสร็จภายในสิบนาทีสำหรับ n = 32 เนื่องจากVec<bool>ไม่เหมาะสำหรับการคำนวณแบบนี้

แก้ไข

  1. ฉันจัดการเพื่อลบข้อ จำกัด ของ 64 nสำหรับ ทีนี้มันก็จะไม่พังจนกว่าnจะมีค่ามากกว่าจำนวนเต็มสูงสุด

  2. ผมพบว่าเหตุผลที่รหัสก่อนหน้านี้กลับ 317 n=32สำหรับ ฉันถูกนับชุดของระยะเวลาและไม่อาร์เรย์งวด มีสามอาร์เรย์ที่มีองค์ประกอบเหมือนกัน:

    [1, 2, 3, 3, 8] -> {1, 2, 3, 8}
    [1, 2, 3, 8, 8] -> {1, 2, 3, 8}
    [1, 1, 3, 3, 7] -> {1, 3, 7}
    [1, 1, 3, 7, 7] -> {1, 3, 7}
    [1, 1, 3, 3, 8] -> {1, 3, 8}
    [1, 1, 3, 8, 8] -> {1, 3, 8}
    

ตอนนี้มันใช้งานได้ มันยังคงช้า แต่ก็ใช้งานได้


ที่นี่มีทั้งหมด 320 n = 16 bpaste.net/show/3664e25ebc01

1
@Lembik ฉันพบคำอธิบายสำหรับ 317 ขอบคุณรายการของคุณ
jferard

2

C - 16

มันล้มเหลวในค่าที่มากกว่า 16 cuz ของล้น

ฉันไม่รู้ว่ามันจะวิ่ง cuz im บน chromebook ได้เร็วแค่ไหนใน repl.it

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

#include "stdio.h"
#include <stdbool.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>

int per(int s[], int l) {
  int period = 0;
  while (1) {
    period++;

    bool check = 1;
    int i;
    for (i=0; i<l-period; i++) {
      if (s[i]!=s[i+period]) {
        check = 0;
        break;
      }
    }
    if (check) {
      return period;
    }
  }
}

bool perar(int* s, int l, int* b, int i) {
  int n = 1;
  int j=0;
  while (n<=l) {
    b[i*l+j] = per(s, n);
    n=n<<1;
    j++;
  }

  for (j=0;j<i;j++) {
    int k;
    bool check = 1;
    for(k=0; k<l; k++) {
      if (b[j*l+k] != b[i*l+k]) {
        check = 0;
        break;
      }
    }
    if (check) {
      return 0;
    }
  }
  return 1;
}

int main(int argc, char* argv[]) {
  int n;
  scanf("%d", &n);
  puts("Running...");
  int i;
  int c = 0;
  int* a = malloc(n*sizeof(int));
  int m=pow(2, n);
  int* b = malloc(m*n*sizeof(int));
  for (i=0; i<m; i++) {
    int j;
    for (j=0; j<n; j++) {
      a[j] = (i>>j)&1;
    }
    c+=perar(a, n, b, i);
  }
  printf("Answer: %d\n", c);
  return 0;
}

เพียงรวบรวมมันด้วย gcc ฯลฯ


FYI - มันเป็นข้อ16ผิดพลาดในตอนนั้นเมื่อมีการเปลี่ยนแปลงรหัสเพื่อให้ทั้งสองmallocเป็นmalloc(...int*))และพิมพ์...**ตามลำดับตามที่คาดไว้อย่างไรก็ตามพิมพ์(และค่อนข้างเร็ว) 16Answer: 32032Answer: 0
Jonathan Allan

@JanathanAllan แก้ไขสิ่งที่ทำเพียงทำให้เป็น int *
Maltysen

@Janathan ทุกสิ่ง 32 คือ cuz 2 ** 32 ล้น int นอกจากนี้ฉันจะพร่องหน่วยความจำหมด
Maltysen

@ ThePirateBay ฉันทำให้ฉันและฉันยาวและนั่นก็แค่ segfaults เมื่อฉันลอง 32. repl.it/JwJl/2ฉันคาดเดาว่าหน่วยความจำของฉันจะหมด
Maltysen

@Maltysen ดูเหมือนว่ามันเป็น segfaults เพราะคุณทำอะไรผิดพลาดในการจัดสรร / การจัดสรรคืนแทนที่จะขาดหน่วยความจำที่มีอยู่ ฉันได้รับ segfault n = 8แต่หลังจากพิมพ์ผลลัพธ์ซึ่งหมายความว่าสแต็กเสียหาย อาจเป็นเพราะคุณกำลังเขียนอยู่นอกบล็อกหน่วยความจำที่จัดสรรไว้

2

Haskell

import qualified Data.Set as S
import Data.Bits

period :: Int -> Int -> Int
period num bits = go (bits-2) (div prefix 2) (clearBit prefix $ bits-1)
  where
  prefix = (2^bits-1) .&. num
  go p x y
    | x == y    = p
    | otherwise = go (p-1) (div x 2) (clearBit y p)

allPeriods :: Int ->  [[Int]]
allPeriods n = map periods [0..div(2^n)2-1]
  where
  periods num = map (period num) powers
  powers = takeWhile (<=n) $ iterate (*2) 2

main = readLn >>= print . S.size . S.fromList . allPeriods

ghc -O2คอมไพล์ด้วย ลองออนไลน์!

วิ่งในเวลาน้อยกว่า 0.1sec บนฮาร์ดแวร์แล็ปท็อปอายุ 6 n=16ปีของฉัน n=32ใช้เวลา99 92 นาทีดังนั้นฉันจึงแยก 9 หรือ 10 ออก ฉันพยายามแคชช่วงเวลาในตารางการค้นหาดังนั้นฉันไม่ต้องคำนวณซ้ำแล้วซ้ำอีก แต่หน่วยความจำนี้หมดอย่างรวดเร็วในเครื่อง 4GB ของฉัน


แม้จะเป็นปัจจัย 10 แต่รหัสของคุณก็ดูดีมาก

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

@Lembik: การเว้น 1 สตริงจะช่วยให้ประหยัดได้ประมาณ 7 นาทีสำหรับ n = 32 ยังคงยาวเกินไป
nimi

มีอัลกอริธึมเชิงเส้นเร็วสำหรับคำนวณระยะเวลาที่อาจช่วยได้

คุณไม่สามารถสร้างตารางค้นหาขนาด 2 ^ 16 ได้จริงหรือ ดูเหมือนจะไม่ใหญ่เกินไป

1

Python 2 (PyPy), 16

import sys
import math
def do(n):
 masks=[]
 for i in range(n):
  masks+=[(1<<((2<<i)-1))-1]
 s=set()
 bits=1<<n
 for i in xrange(1<<bits):
  r=[0,]*n
  for j in range(len(masks)):
   mask=masks[j]
   k,c=i>>bits-(2<<j),1
   d=k>>1
   while k&mask^d:
    d>>=1
    mask>>=1
    c+=1
   r[j]=c
  s|={tuple(r)}
 return len(s)
print do(int(math.log(int(sys.argv[1]),2)))

: | เหตุใด 32 จึงต้องใช้เวลานาน
เฉพาะ ASCII เท่านั้น

ฉันรู้ว่าฉันสามารถข้ามครึ่งหนึ่งของพวกเขา แต่ IDK อย่างไร: /
ASCII เท่านั้น

รหัสของคุณดูเหมือนจะส่งออกเฉพาะ "ไม่มี" สำหรับฉัน คุณเป็นอย่างไรบ้าง osboxes@osboxes:~/python$ python ascii_user.py 16 None

อึขออภัยนี่ไม่ใช่สิ่งที่ฉันเรียก
ASCII- เท่านั้น

@Lembik ได้รับการแก้ไขแล้ว
เฉพาะ ASCII เท่านั้น

1

[C ++], 32, 4 นาที

#include <iostream>
#include <vector>

typedef unsigned int u;
template<typename T, typename U>
u Min(T a, U b) {
    return a < b ? a : b;
}

template<typename T, typename U>
u Max(T a, U b) {
    return a > b ? a : b;
}

u Mask(int n) {
    if (n < 0) n = 0;
    return ~((u)(-1) << n);
}
u MASKS[32];

inline u Rshift(u v, int n) {
    return n < 0 ? v >> (-1*n)
    : n > 0 ? v << n
    : n;
}

int GetNextPeriodId(u pattern, int pattern_width, int prior_id) {
    int period = (prior_id % (pattern_width>>1)) + 1;
    int retval = prior_id * pattern_width;

    for (; period < pattern_width; period+=1) {
        u shift = pattern >> period;
        int remainder = pattern_width-period;
        u mask = MASKS[period];

        for (;remainder >= period && !((pattern ^ shift) & mask);
             shift >>= period, remainder -= period);

        if (remainder > period) continue;
        if (remainder == 0 || !((pattern ^ shift) & MASKS[remainder])) {
            retval += (period-1);
            break;
        }
    }
    if (period == pattern_width) {
        retval += pattern_width-1;
    }
    return retval;
}

int ParseInput(int argc, char** argv) {
    if (argc > 1) {
        switch(atoi(argv[1])) {
            case 1:
                return 1;
            case 2:
                return 2;
            case 4:
                return 4;
            case 8:
                return 8;
            case 16:
                return 16;
            case 32:
                return 32;
            default:
                return 0;
        }
    }
    return 0;
}

void PrintId(u id, int patternWidth) {
    for(;patternWidth > 0; id /= patternWidth, patternWidth >>= 1) {
        std::cout << (id % patternWidth)+1 << ",";
    }
    std::cout << std::endl;
}

int TestAndSet(std::vector<bool>& v, int i) {
    int retval = v[i] ? 0 : 1;
    v[i] = true;
    return retval;
}

std::vector<bool> uniques(1<<15);
int uniqueCount = 0;

void FillUniques(u i, int id, int target_width, int final_width) {
    int half_size = target_width / 2;
    u end = 1u<<(half_size-1);
    u mask = MASKS[half_size];
    u lowers[] = { i, (~i)&mask };
    for (u j = 0ul; j < end; j++) {
        u upper = j << half_size;
        u patterns[] = { (upper|lowers[0]), (upper|lowers[1]) };
        for (int k=0; k < sizeof(patterns)/sizeof(patterns[0]); k+=1) {
            int fid = GetNextPeriodId(patterns[k], target_width, id);
            if (target_width != final_width) {
                FillUniques(patterns[k], fid, target_width*2, final_width);
            } else {
                if (TestAndSet(uniques, fid)) {
                    uniqueCount += 1;
                }
            }
        }
    }
}

int main(int argc, char** argv) {
    for (int i = 0; i < 32; i++) {
        MASKS[i] = Mask(i);
    }
    int target_width = 32; // ParseInput(argc, argv);
    if (!target_width) {
        std::cout << "Usage: " << argv[0] << " [1|2|4|8|16|32]" << std::endl;
        return 0;
    }
    if (target_width == 1) {
        std::cout << 1 << std::endl;
        return 0;
    }
    FillUniques(0, 0, 2, target_width);
    std::cout << uniqueCount << std::endl;
    return 0;
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.