计算周期数组


11

period一个字符串的是最短的非零移位,使得字符串匹配本身,忽略任何份该突出端。因此,例如,abcabcab具有period 3。按照惯例,如果没有这种移位,则字符串的周期等于其长度。因此,时期abcde5和时期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]

对于给定的2的正幂n,请考虑所有可能的二进制字符串S。回想一下,二进制字符串只是1s和0s 的字符串,因此恰好有2^n这样的字符串(即2n)。对于每个,我们都可以计算这一周期数组。

挑战在于编写以n(2的幂)为输入的代码,并计算有多少个此类数组。

的答案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中运行/编译代码的完整说明。

您的代码实际上应该计算答案,而不是例如仅输出预先计算的值。

领先的作品

  • Peter Taylor 在C#中n = 128时为2分21秒
  • ISAACG 在Rust中n = 32时为9秒

这使我的头部受伤。
亨利

1
挑战很有趣,但是我仍然看不到您用来区分“预先计算”“实际计算”答案的客观标准。例如,如果您不能理解我的代码的工作原理,但是它给出了正确的答案n,那么您会接受吗?硬编码和实际计算之间的边界在哪里还没有很好地定义。


1
@ThePirateBay codegolf.meta.stackexchange.com/a/1063/9206。这是一个标准规则。

2
@Cowsquack字符串的前三个字母除外abcab。除了最后3个字母以外的都是abcab。这些匹配,并且删除较少数量的字母不匹配。
isaacg '17

Answers:


9

C#,大约在2:40时n = 128

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-csc和执行mono

基本说明

我不会逐行剖析,只是概念的概述。

根据经验,我很乐意在蛮力组合程序中遍历2 50个元素。因此,要达到n = 128,必须使用一种无​​法分析每个位串的方法。因此,我不是从位串到周期序列进行工作,而是向后工作:给定一个周期序列,是否有位串可以实现它?对于n = 2 x,有2 x(x + 1)/ 2个周期序列(相对于2 2 x位串)的上限。

一些参数使用字符串周期性引理

pq为一串长度的两个周期n。如果p + q ≤ n + gcd(p, q)gcd(p, q)也是一个时期的字符串。

我将假定正在考虑的所有位串均以开头0

给定一个周期序列,其中是长度为2 i的前缀的周期(始终)(始终),可以很容易地观察到以下可能的值:[p1 p2 ... pk]pip0 = 1pk+1

  • pk+1 ≥ pk因为字符串S的句点也是前缀的句点S

  • pk+1 = pk 总是可能的扩展:只需将相同的原始字符串重复两倍的字符。

  • 2k < pk+1 ≤ 2k+1始终是可能的扩展。足以证明这一点,因为可以通过添加不是首字母的任意字母来将非周期性长度字符串扩展为非周期性长度字符串。pk+1 = 2k+1LL+1

    取一个Sx长度为2 k的字符串,其周期为,并考虑一个长度为2 k + 1的字符串。显然有一个周期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和是的周期,所以length的前缀只是length前缀的副本,因此我们看到也是的周期。pkSxqq/pkpkpkSxyS

    因此的周期SxyS或者是或2 ķ 1。但是我们有两个选择!最多选择一个会给定周期,因此至少会有一个会给定周期2 k +1。QED。pkyypk

  • 周期性引理使我们可以拒绝某些剩余的可能扩展。

  • 任何未通过快速接受或快速拒绝测试的扩展都必须进行建设性测试。

给定周期序列的位串的构造本质上是可满足性的问题,但是它具有很多结构。每个前缀周期都暗示着简单的相等约束,因此我使用联合集数据结构将位组合为独立的簇。这足以解决n = 64的问题,但对于n = 128的情况,则有必要走得更远。我使用了两个有用的论点:2k - pk

  1. 如果length的前缀M是,并且length 的前缀具有句点,则length的前缀必须以结尾。恰恰在这种情况下,如果本来会具有最独立的群集,则功能最强大,这很方便。01M-1L > MLL1M
  2. 如果长度的前缀M是和长度的前缀具有周期与和端部然后它必须实际上端中。相反,当周期序列以很多开头时,这是最强大的。0ML > ML - dd < M0d10d

如果我们没有通过强制将第一个比特(假定为零)的群集设为1来立即产生矛盾,那么我们将对一些非强制群集的可能值进行蛮力(带有一些微优化)。请注意,顺序是按降序排列的,因为如果第ith位为1,则周期不能为1,i并且我们要避免使用比群集已强制执行的周期短的周期。失败会增加尽早找到有效任务的机会。


这是一个了不起的成就!我印象深刻。

@Lembik,我已经简化和优化了代码,并将n = 128的运行时间减少了约三分之一。
彼得·泰勒

1
我很想知道您为此设计了什么算法。您的代码中几乎没有逻辑,并且必须做得非常聪明。

7

Rust,32,10s 11s 29s在我的笔记本电脑上

以bitsize作为命令行参数调用它。

巧妙的技巧:将位串直接表示为数字,使用位扭曲检查周期。只搜索比特串的前半部分,即以0开头的比特串,因为比特串的周期数组及其倒数(0交换为1)是相同的。如果最终职位的所有可能性已经发生,我不会搜索它。

更聪明的东西:

为了对每个块(位的前半部分相同的字符串)进行重复数据删除,我使用了一个位向量,它比哈希集要快得多,因为最终循环长度不需要哈希。

另外,我跳过了循环检查的第一步,因为我知道最终循环不能短于倒数第二个循环。

经过大量的分析后,我现在可以说几乎所有时间都被有效地使用,因此,我认为从现在开始需要改进算法。我还切换到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之后需要一个rust版本。如果我将其更改为println,它将起作用。

这个答案非常令人印象深刻。time在我的笔记本电脑上为用户提供了27个用户秒。

@Lembik为什么您会遇到这种旧版本的生锈?Rust 1.0于几年前问世。
isaacg '17

错字:)我的意思是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

该字符串被实现为布尔向量。

  • next通过组合功能迭代;

  • find_period需要一个布尔切片,并返回的期间;

  • 对于compute_count_arrayBools的每个组合的每个“二的幂”子序列,该函数都起作用。

从理论上讲,除非2^n超过u64最大值(即),否则不会发生溢出n > 64。可以通过对s = [true,true,...,true]进行昂贵的测试来超越此限制。

坏消息是:对于n = 16,它返回317,但我不知道为什么。我也不知道它是否会在10分钟内达到n = 32,因为Vec<bool>尚未针对这种计算进行优化。

编辑

  1. 我设法删除的64个限制n。现在,直到n大于最大使用整数,它才会崩溃。

  2. 我发现了为什么先前的代码会为317返回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}
    

现在可以了。它仍然很慢,但是有效。


这是n = 16 bpaste.net/show/3664e25ebc01的所有320个。

1
@Lembik由于您的列表,我找到了317的解释。
jferard '17

2

C-16

如果溢出值大于16 cuz,它将失败。

我不知道这在chromebook上的repl.it上运行cuz im有多快。

只需在读取时实施问题,遍历所有位字符串,计算周期数组并检查是否已对其进行计数即可。

#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等进行编译


仅供参考-这被示数为16则当代码被更改,以便这两个malloc小号是malloc(...int*))...**分别16打印Answer: 320预期,但32印刷Answer: 0(和很快)。
乔纳森·艾伦,

@JonathanAllan修复了这些内容,只是将b设置为int *。
Maltysen '17年

@JonathanAllan的32件事是cuz 2 ** 32溢出了int。另外,我会耗尽内存。
马蒂森(Maltysen)

@ThePirateBay我让我和m多了,而当我尝试32时,这只是段错误。repl.it/JwJl/2我猜想它用完了内存。
马蒂森(Maltysen)

@Maltysen。看来它是段错误的,因为您弄乱了分配/取消分配中的某些内容,而不是缺少可用内存。我遇到了段错误,n = 8但是在结果打印后,这意味着堆栈已损坏。可能您正在写超出分配的内存块的地方。

2

哈斯克尔

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在线尝试!

我6岁的笔记本电脑硬件运行不到0.1秒n=16n=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码,仅ASCII

我知道我可以跳过其中一半,但IDK可以:/
ASCII码仅ASCII

您的代码似乎只为我输出“ None”。你好吗?osboxes@osboxes:~/python$ python ascii_user.py 16 None

废话抱歉,这实际上不是我所运行的
ASCII码,仅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;
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.