Đếm mảng thời gian


11

Các periodcủa một chuỗi là khác không thay đổi ngắn nhất để các chuỗi phù hợp với bản thân, bỏ qua bất kỳ bộ phận nhô ra. Vì vậy, ví dụ, abcabcabcó thời gian 3. Theo quy ước, chúng tôi nói rằng nếu không có sự dịch chuyển như vậy thì một chuỗi có chu kỳ bằng độ dài của nó. Vì vậy, thời kỳ abcde5và thời kỳ a1.

Trong các điều khoản chính thức hơn, khoảng thời gian của một chuỗi Slà tối thiểu i > 0sao cho S[1,n-i] == S[i+1,n](lập chỉ mục từ 1).

Đối với một chuỗi S có công suất có hai độ dài, chúng ta sẽ tính khoảng thời gian của tất cả các tiền tố có độ dài hai chiều. Ví dụ, xem xét S = abcabcab. Các giai đoạn chúng tôi sẽ tính toán là:

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

Trong thực tế, chúng ta sẽ chỉ xuất ra các mảng thời gian, đó là [1, 2, 3, 3].

Đối với một sức mạnh tích cực nhất định của hai n, hãy xem xét tất cả các chuỗi nhị phân có thể S. Hãy nhớ lại rằng một chuỗi nhị phân chỉ đơn giản là một chuỗi 1s và 0s để có chính xác 2^ncác chuỗi như vậy (đó là 2sức mạnh n). Đối với mỗi một chúng ta có thể tính toán mảng thời gian này.

Thách thức là viết mã lấy n(một lũy thừa hai) làm đầu vào và tính toán có bao nhiêu mảng khác nhau như vậy.

Câu trả lời cho n = 1, 2, 4, 8, 16, 32, 64, 128:

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

Toàn bộ các mảng thời gian riêng biệt cho n = 4là:

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

Ghi bàn

Tôi sẽ chạy mã của bạn trên máy tính chạy Ubuntu trong 10 phút. Điểm của bạn là lớn nhất nmà mã của bạn chấm dứt trong thời gian đó. Trong trường hợp hòa, câu trả lời hoàn thành nchiến thắng nhanh nhất chung lớn nhất. Trong trường hợp có một ràng buộc trong vòng 1 giây về thời gian, câu trả lời đầu tiên được đăng sẽ thắng.

Ngôn ngữ và thư viện

Bạn có thể sử dụng bất kỳ ngôn ngữ và thư viện có sẵn mà bạn thích. Vui lòng bao gồm một lời giải thích đầy đủ về cách chạy / biên dịch mã của bạn trong Linux nếu có thể.

Mã của bạn thực sự nên tính toán các câu trả lời và không, ví dụ, chỉ xuất các giá trị được tính toán trước.

Mục hàng đầu

  • 2 phút và 21 giây cho n = 128 trong C # của Peter Taylor
  • 9 giây cho n = 32 trong Rust bởi isaacg

Điều này làm cho đầu tôi đau.
Henry

1
Thử thách này rất thú vị, nhưng tôi vẫn không thể thấy tiêu chí khách quan mà bạn đang sử dụng để phân biệt giữa câu trả lời "được tính toán trước""thực sự được tính toán" . Nếu bạn, ví dụ, không thể hiểu làm thế nào mã của tôi hoạt động, nhưng nó cho câu trả lời chính xác rất lớn n, bạn có chấp nhận nó không? Nó không được xác định rõ đâu là ranh giới giữa mã hóa cứng và điện toán thực tế.


1
@ThePirateBay codegolf.meta.stackexchange.com/a/1063/9206 . Đó là một quy tắc chuẩn.

2
@Cowsquack Tất cả trừ ba chữ cái đầu tiên của chuỗi là abcab. Tất cả trừ 3 chữ cái cuối cùng là abcab. Những kết hợp này và loại bỏ một số lượng nhỏ hơn các chữ cái không khớp.
isaacg

Câu trả lời:


9

C #, n = 128 trong khoảng 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;
            }
        }
    }
}

Việc mở rộng đến n = 256 sẽ yêu cầu chuyển sang BigIntegercho mặt nạ, điều này có thể giết chết hiệu suất quá nhiều để n = 128 vượt qua mà không có ý tưởng mới, chứ đừng nói đến n = 256.

Trong Linux, biên dịch với mono-cscvà thực thi với mono.

Giải thích cơ bản

Tôi sẽ không tiến hành mổ xẻ từng dòng, chỉ là một cái nhìn tổng quan về các khái niệm.

Như một quy luật tự nhiên, tôi rất vui khi lặp lại theo thứ tự 2 50 yếu tố trong một chương trình kết hợp vũ phu. Do đó, để có được n = 128, cần sử dụng một cách tiếp cận không phân tích mọi chuỗi bit. Vì vậy, thay vì làm việc chuyển tiếp từ chuỗi bit sang chuỗi thời gian, tôi làm việc ngược lại: đưa ra một chuỗi thời gian, có một chuỗi bit nào nhận ra điều đó không? Với n = 2 x có giới hạn trên dễ dàng là 2 x (x + 1) / 2 chuỗi thời gian (so với 2 2 x bitstrings).

Một số đối số sử dụng bổ đề định kỳ chuỗi :

Cho pqlà hai khoảng thời gian của một chuỗi độ dài n. Nếu p + q ≤ n + gcd(p, q)sau đó gcd(p, q)cũng là một khoảng thời gian của chuỗi.

Wlog tôi sẽ giả định rằng tất cả các bitstr đang xem xét bắt đầu với 0.

Cho một chuỗi thời gian trong đó là khoảng thời gian của tiền tố có độ dài 2 i ( luôn luôn), có một số quan sát dễ dàng về các giá trị có thể có của :[p1 p2 ... pk]pip0 = 1pk+1

  • pk+1 ≥ pkvì một khoảng thời gian của chuỗi Scũng là khoảng thời gian của bất kỳ tiền tố nào S.

  • pk+1 = pk luôn luôn là một phần mở rộng có thể: chỉ cần lặp lại cùng một chuỗi nguyên thủy cho số nhân vật nhiều gấp đôi.

  • 2k < pk+1 ≤ 2k+1luôn luôn là một phần mở rộng có thể. Nó đủ cho thấy điều này bởi vì một chuỗi độ dài có thể được mở rộng thành một chuỗi độ dài có chu kỳ bằng cách nối thêm bất kỳ chữ cái nào không phải là chữ cái đầu tiên.pk+1 = 2k+1LL+1

    Lấy một chuỗi Sxcó độ dài 2 k có chu kỳ là và xem xét chuỗi có độ dài 2 k + 1 . Rõ ràng có một khoảng thời gian 2 k 1. Giả sử thời gian của nó nhỏ hơn.pkSxySSxySq

    Do đó, bổ đề định kỳ cũng là một khoảng thời gian và vì ước số lớn nhất nhỏ hơn hoặc bằng các đối số của nó và là khoảng thời gian nhỏ nhất, chúng tôi yêu cầu phải là hệ số thích hợp của 2 k +1. Vì thương số của nó không thể là 2, chúng tôi có .2k+1 + q ≤ 2k+1+1 ≤ 2k+1 + gcd(2k+1, q)gcd(2k+1, q)SxySqqq ≤ (2k+1)/3

    Bây giờ, vì là một khoảng thời gian của nó phải là một khoảng thời gian . Nhưng những khoảng thời gian là . Chúng tôi có hai trường hợp:q ≤ 2kSxySSxSxpk

    1. gcd(pk, q) = pkhoặc tương đương chia chính xác thành .pkq
    2. pk + q > 2k + gcd(pk, q) do đó bổ đề định kỳ không buộc một thời kỳ nhỏ hơn.

    Hãy xem xét trường hợp thứ hai. , mâu thuẫn với định nghĩa là thời kỳ . Vì vậy, chúng tôi buộc phải đưa ra kết luận đó là một yếu tố của .pk > 2k + gcd(pk, q) - q ≥ 2k+1 - q ≥ 2k+1 - (2k+1)/3 ≥ 2qpkSxpkq

    Nhưng vì qlà một khoảng thời gian Sxvà là khoảng thời gian , tiền tố của độ dài chỉ là bản sao của tiền tố độ dài , vì vậy chúng tôi thấy đó cũng là một khoảng thời gian .pkSxqq/pkpkpkSxyS

    Do đó, khoảng thời gian SxySlà hoặc 2 k +1. Nhưng chúng tôi có hai lựa chọn cho ! Nhiều nhất một lựa chọn sẽ cho thời gian , vì vậy ít nhất một lựa chọn sẽ cho giai đoạn 2 k +1. QED.pkyypk

  • Bổ đề định kỳ cho phép chúng tôi từ chối một số phần mở rộng có thể còn lại.

  • Bất kỳ tiện ích mở rộng nào chưa vượt qua thử nghiệm chấp nhận nhanh hoặc từ chối nhanh cần phải được kiểm tra một cách xây dựng.

Việc xây dựng một chuỗi bit cho một chuỗi thời gian về cơ bản là một vấn đề có thể bão hòa, nhưng nó có rất nhiều cấu trúc. Có các ràng buộc đẳng thức đơn giản được ngụ ý theo từng giai đoạn tiền tố, vì vậy tôi sử dụng cấu trúc dữ liệu tập hợp để kết hợp các bit thành các cụm độc lập. Điều này là đủ để giải quyết n = 64, nhưng với n = 128 thì cần phải đi xa hơn. Tôi sử dụng hai dòng lập luận hữu ích:2k - pk

  1. Nếu tiền tố của độ dài Mlà và tiền tố của độ dài có dấu chấm thì tiền tố của độ dài phải kết thúc bằng . Điều này là mạnh nhất chính xác trong các trường hợp nếu không có hầu hết các cụm độc lập, thuận tiện.01M-1L > MLL1M
  2. Nếu tiền tố có độ dài Mlà và tiền tố có độ dài có thời gian với và kết thúc ở sau đó nó phải trong thực tế kết thúc trong . Điều này là mạnh nhất ở thái cực ngược lại, khi chuỗi thời gian bắt đầu với rất nhiều cái.0ML > ML - dd < M0d10d

Nếu chúng ta không có mâu thuẫn ngay lập tức bằng cách buộc cụm có bit đầu tiên (được giả định là 0) thì chúng ta sẽ tạo ra lực lượng (với một số tối ưu hóa vi mô) đối với các giá trị có thể có cho các cụm không được ép buộc. Lưu ý rằng thứ tự là số lượng giảm dần bởi vì nếu bit ithứ nhất là một thì thời gian không thể ivà chúng tôi muốn tránh các khoảng thời gian ngắn hơn các giai đoạn đã được thực thi bởi phân cụm. Đi xuống làm tăng cơ hội tìm được một bài tập hợp lệ sớm.


Đây là một thành tựu thực sự tuyệt vời! Tôi rất ấn tượng.

@Lembik, tôi đã đơn giản hóa và tối ưu hóa mã và giảm thời gian chạy cho n = 128 khoảng một phần ba.
Peter Taylor

1
Tôi rất muốn biết bạn đã thiết kế thuật toán nào cho việc này. Mã của bạn có rất ít logic trong đó và phải làm một cái gì đó rất thông minh.

7

Rust, 32, 10s 11s 29s trên máy tính xách tay của tôi

Gọi nó với bitize làm đối số dòng lệnh.

Kỹ thuật thông minh: biểu diễn bitstrings trực tiếp dưới dạng số, sử dụng bittwiddling để kiểm tra chu kỳ. Chỉ tìm kiếm nửa đầu của bitstrings, những số bắt đầu bằng 0, bởi vì các chuỗi chu kỳ của chuỗi bit và nghịch đảo của nó (0 được hoán đổi trong 1 giây) là giống hệt nhau. Nếu mọi khả năng cho vị trí cuối cùng đã xảy ra, tôi không tìm kiếm nó.

Công cụ thông minh hơn:

Để sao chép từng khối (các chuỗi có nửa bit đầu tiên giống nhau) Tôi sử dụng bitvector, nhanh hơn nhiều so với hàm băm, vì độ dài chu kỳ cuối không cần băm.

Ngoài ra, tôi bỏ qua các bước đầu tiên của kiểm tra chu kỳ, bởi vì tôi biết rằng chu trình cuối cùng không thể ngắn hơn chu kỳ thứ hai đến cuối cùng.

Sau nhiều hồ sơ, bây giờ tôi có thể nói rằng hầu như tất cả thời gian đang được sử dụng một cách hiệu quả, vì vậy tôi cần phải cải thiện thuật toán để cải thiện từ đây, tôi nghĩ vậy. Tôi cũng chuyển sang số nguyên 32 bit để tiết kiệm thời gian hơn một chút.

//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()
    );
}

Đặt bit-vec = "0.4.4"trong Cargo.toml của bạn

Nếu bạn muốn chạy cái này, hãy sao chép cái này: github.com/isaacg1/ Motorcycle sau đó Cargo build --releaseđể xây dựng, sau đó Cargo run --release 32chạy.


Có vẻ như eprintln cần một phiên bản rỉ sét sau 0.16.0. Nó hoạt động nếu tôi thay đổi nó thành println.

Câu trả lời này rất ấn tượng. timecung cấp cho nó 27 giây người dùng trên máy tính xách tay của tôi.

@Lembik tại sao bạn lại ở một phiên bản cũ như vậy? Rust 1.0 xuất hiện từ nhiều năm trước.
isaacg


Đối với những người mới bị rỉ sét, bạn có phiền đánh vần chính xác cách biên dịch mã bằng hàng hóa không?

4

Mộc , 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
}

Hãy thử trực tuyến!

Tổng hợp: rustc -O <name>.rs

Chuỗi được thực hiện như một vector Bool.

  • Các nextchức năng lặp thông qua sự kết hợp;

  • Việc find_periodlấy một lát Bool và trả về dấu chấm;

  • Công compute_count_arrayviệc thực hiện cho mỗi "sức mạnh của hai" sau đó của mỗi kết hợp Bools.

Về mặt lý thuyết, không có tràn nào được dự kiến ​​cho đến khi 2^nvượt quá giá trị tối đa u64, tức là n > 64. Giới hạn này có thể được vượt quá với một bài kiểm tra đắt tiền về s = [true, true, ..., true].

Tin xấu là: nó trả về 317 cho n = 16, nhưng tôi không biết tại sao. Tôi cũng không biết liệu nó sẽ thực hiện trong mười phút cho n = 32 hay không, vì Vec<bool>nó không được tối ưu hóa cho loại tính toán này.

BIÊN TẬP

  1. Tôi đã quản lý để loại bỏ giới hạn 64 cho n. Bây giờ, nó sẽ không sụp đổ cho đến khi nlớn hơn số nguyên sử dụng tối đa.

  2. Tôi tìm thấy lý do tại sao mã trước đó trả về 317 cho n=32. Tôi đã đếm các bộ thời gian và không phải là mảng của thời kỳ. Có ba mảng có cùng các phần tử:

    [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}
    

Bây giờ nó hoạt động. Nó vẫn chậm nhưng nó hoạt động.


Dưới đây là tất cả 320 cho n = 16 bpaste.net/show/3664e25ebc01 .

1
@Lembik Tôi tìm thấy lời giải thích cho 317 nhờ vào danh sách của bạn.
jferard

2

C - 16

Nó thất bại trên các giá trị lớn hơn 16 cuz tràn.

Tôi không biết cái này chạy nhanh như thế nào vì tôi đang chạy trên chromebook chạy nó trên repl.it.

Chỉ cần thực hiện câu hỏi khi nó đọc, đi qua tất cả các chuỗi bit, tính toán các mảng thời gian và kiểm tra xem chúng đã được tính chưa.

#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;
}

Chỉ cần biên dịch nó với gcc, vv


FYI - Nó được erroring cho 16sau đó khi mã đã được thay đổi để hai mallocs đã malloc(...int*))...**lần lượt 16được in Answer: 320như mong đợi, tuy nhiên 32in Answer: 0(và khá nhanh chóng).
Jonathan Allan

@Jonathan ALLan đã sửa các công cụ, chỉ cần tạo một int *.
Maltysen

@Jonathan ALLan 32 điều là cuz 2 ** 32 tràn vào int. Ngoài ra tôi sẽ hoàn toàn hết bộ nhớ.
Maltysen

@ThePirateBay tôi đã làm cho tôi và tôi khao khát, và điều đó chỉ xảy ra khi tôi thử 32. repl.it/JwJl/2 Tôi đoán nó hết bộ nhớ.
Maltysen

@Maltysen. Có vẻ như nó tách biệt vì bạn đã làm hỏng thứ gì đó trong phân bổ / giải quyết thay vì thiếu bộ nhớ khả dụng. Tôi đã nhận được segfault n = 8nhưng sau khi kết quả được in, điều đó có nghĩa là ngăn xếp bị hỏng. Có lẽ bạn đang ở đâu đó viết vượt quá (các) khối bộ nhớ được phân bổ.

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

Biên dịch với ghc -O2. Hãy thử trực tuyến!

Chạy trong chưa đầy 0,1 giây trên phần cứng máy tính xách tay 6 tuổi của tôi n=16. n=32mất 99 92 phút, vì vậy tôi yếu tố 9 hoặc 10. Tôi đã thử lưu trữ các khoảng thời gian trong bảng tra cứu để tôi không phải tính toán lại chúng nhiều lần, nhưng điều này nhanh chóng hết bộ nhớ trên máy 4GB của tôi.


Mặc dù là một yếu tố của 10 tắt, mã của bạn rất đẹp.

@Lembik. Cảm ơn. Tôi chỉ đang thử một cải tiến: đoạn mã trên tính toán các khoảng thời gian cho các chuỗi con có độ dài 1, nhưng điều đó hoàn toàn không cần thiết. Bên cạnh việc không phải tính toán chúng, nó cũng tiết kiệm thời gian khi tìm các mảng thời gian duy nhất, bởi vì tất cả chúng đều ngắn hơn một yếu tố.
nimi

@Lembik: bỏ qua chiều dài 1 chuỗi con tiết kiệm khoảng 7 phút cho n = 32. Vẫn còn quá dài.
nimi

Có một thuật toán tuyến tính nhanh để tính toán giai đoạn có thể giúp ích.

Bạn thực sự không thể xây dựng một bảng tra cứu kích thước 2 ^ 16? Điều đó dường như không quá lớn.

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)))

: | tại sao 32 phải mất nhiều thời gian như vậy
ASCII - chỉ

Tôi biết tôi có thể bỏ qua một nửa trong số họ nhưng IDK như thế nào: /
ASCII

Mã của bạn dường như chỉ xuất "Không" cho tôi. Làm thế nào bạn đang chạy nó? osboxes@osboxes:~/python$ python ascii_user.py 16 None

crap xin lỗi đây không thực sự là những gì tôi chạy
ASCII - chỉ

@Lembik đã sửa ngay bây giờ
ASCII - chỉ

1

[C ++], 32, 4 phút

#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;
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.