周期の配列を数える


11

period文字列は、任意の部品そのオーバーハングを無視し、自身と一致するように文字列の最短非ゼロのシフトです。そのため、たとえばabcabcabperiodがあり3ます。慣例により、このようなシフトがない場合、文字列の長さはピリオドに等しいと言います。期間だから、abcdeIS 5との期間aです1

より正式な用語では、文字列の期間はS最小であるi > 0ためS[1,n-i] == S[i+1,n](からインデックス付け1)。

2の長さの累乗の特定の文字列Sについて、2の長さの累乗のすべてのプレフィックスの周期を計算します。たとえば、検討してくださいS = abcabcab。計算する期間は次のとおりです。

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

実際には、ピリオドの配列、つまりを出力するだけです[1, 2, 3, 3]

2の与えられた正のべきのために、nすべての可能なバイナリストリングを考えてくださいS。バイナリ文字列は単に1sと0sの文字列であるため、まさに2^nそのような文字列が存在することを思い出してください(つまり2、べきですn)。それぞれについて、この周期の配列を計算できます。

課題は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でコードを実行/コンパイルする方法について可能な限り完全な説明を含めてください。

コードは実際に答えを計算する必要があり、たとえば、事前に計算された値を出力するだけではありません。

主要なエントリー

  • 2分21秒のために、N = 128におけるC#ピーター・テイラー
  • 9秒のため、N = 32さび isaacgによって

これで頭が痛くなった。
ヘンリー

1
このチャレンジは興味深いものですが、「事前計算済み」「実際に計算された」答えを区別するために使用している客観的な基準はまだわかりません。たとえば、私のコードがどのように機能するか理解できない場合でも、hugeに対して正しい答えが得られる場合、それをn受け入れますか?ハードコーディングと実際のコンピューティングの境界がどこにあるかは明確に定義されていません。


1
@ThePirateBay codegolf.meta.stackexchange.com/a/1063/9206。これは標準的なルールです。

2
@Cowsquack文字列の最初の3文字以外はすべてですabcab。最後の3文字以外はすべてですabcab。これらは一致し、少数の文字を削除しても一致しません。
-isaacg

回答:


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 = 256はもちろんのこと、n = 128が新しいアイデアなしで通過するにはパフォーマンスが大幅に低下する可能性があります。

Linuxでは、でコンパイルしmono-cscて実行しmonoます。

基本的な説明

行ごとの分析は行わず、概念の概要のみを説明します。

経験則として、ブルートフォースの組み合わせプログラムで2〜50個の要素を繰り返し処理できます。したがって、n = 128に到達するには、すべてのビット列を分析しないアプローチを使用する必要があります。ですから、ビット列から周期シーケンスに進むのではなく、逆に動作します:周期シーケンスが与えられた場合、それを実現するビット文字列はありますか?n = 2 xの場合、2 x(x + 1)/ 2周期シーケンスの簡単な上限があります(vs 2 2 xビット文字列)。

引数のいくつかは、文字列の周期性補題を使用します

させるpおよびqの長さの文字列の2つの期間ですn。もしp + q ≤ n + gcd(p, q)、その後gcd(p, q)も、文字列の周期です。

Wlog検討中のすべてのビット文字列はで始まると仮定し0ます。

長さ2 i(常に)の接頭辞の周期である周期シーケンスが与えられると、可能な値についていくつかの簡単な観察があります:[p1 p2 ... pk]pip0 = 1pk+1

  • pk+1 ≥ pk文字列SのピリオドものプレフィックスのピリオドであるためS

  • pk+1 = pk は常に可能な拡張です。同じプリミティブ文字列を2倍の文字数だけ繰り返します。

  • 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

    さて、の期間はの期間でなければなりません。しかし期間はです。次の2つのケースがあります。q ≤ 2kSxySSxSxpk

    1. gcd(pk, q) = pk、または同等に正確にに分割します。pkq
    2. pk + q > 2k + gcd(pk, q) 周期性補題がより短い期間を強制しないように。

    最初に2番目のケースを検討してください。の期間としての定義に矛盾しています。したがって、私たちは要因である結論に強制されます。pk > 2k + gcd(pk, q) - q ≥ 2k+1 - q ≥ 2k+1 - (2k+1)/3 ≥ 2qpkSxpkq

    しかし、はのq期間でSxあり、の期間であるため、長さのプレフィックスは長さのプレフィックスの単なるコピーであるため、これも期間であることがわかります。pkSxqq/pkpkpkSxyS

    そのための期間は、SxySいずれかであるか、2 K +1。しかし、次の2つのオプションがあります!最大で1つの選択肢から期間が与えられるため、少なくとも1 つの選択肢で期間2 k +1 が与えられます。QED。pkyypk

  • 周期性補題により、残りの拡張の一部を拒否できます。

  • クイック受け入れテストまたはクイック拒否テストに合格していない拡張機能は、建設的にテストする必要があります。

周期シーケンスを指定したビット文字列の構築は、本質的に充足可能性の問題ですが、多くの構造を持っています。各プレフィックス期間によって暗示される単純な等式制約があるため、ユニオンセットデータ構造を使用して、ビットを独立したクラスターに結合します。これはn = 64に取り組むには十分でしたが、n = 128の場合はさらに先へ進む必要がありました。私は2つの有用な議論の行を採用しています:2k - pk

  1. 長さのプレフィックスがいる場合Mであると長さのプレフィックスが期間を持って、その後長さの接頭辞がで終わらなければなりません。これは、そうでなければ最も独立したクラスターを持つ場合に最も正確であり、便利です。01M-1L > MLL1M
  2. 長さのプレフィックスは場合Mですと長さの接頭辞は、期間があるとして、終了する、それが実際の最後にでなければなりません。これは、ピリオドシーケンスが多くのピリオドで始まる場合の反対の極端な場合に最も強力です。0ML > ML - dd < M0d10d

クラスターを最初のビット(ゼロと仮定)を1に強制することで即座に矛盾が生じない場合、強制されていないクラスターの可能な値に対してブルートフォース(いくつかのミクロ最適化)を行います。順序は1の降順であることに注意してくださいi。thビットが1の場合、期間はできずi、クラスタリングによって既に実施されている期間よりも短い期間を避けたいためです。下がると、有効な課題を早期に見つける可能性が高くなります。


これは本当に素晴らしい成果です!すごく感動しました。

@Lembik、コードを簡素化および最適化し、n = 128の実行時間を約3分の1短縮しました。
ピーターテイラー

1
このために設計したアルゴリズムを知りたいと思います。あなたのコードには非常に小さなロジックが含まれており、非常に賢い何かをしているに違いありません。

7

さび、32、私のラップトップで10秒1129

コマンドライン引数としてビットサイズを指定して呼び出します。

巧妙なテクニック:ビット文字列を直接数値として表現し、bittwiddlingを使用してサイクルをチェックします。ビット列の周期の配列とその逆数(0が1にスワップされる)は同一であるため、ビット文字列の前半(0で始まるもの)のみを検索します。最終的なポジションの可能性がすべて既に発生している場合は、検索しません。

より賢いもの:

各ブロック(ビットの前半が同じ文字列)を重複排除するには、ビットベクトルを使用します。これは、ハッシュセットよりもはるかに高速です。最終サイクルの長さにはハッシュが必要ないからです。

また、最後のサイクルは最後から2番目のサイクルよりも短くてはならないことがわかっているため、サイクルチェックの最初のステップをスキップします。

多くのプロファイリングの後、ほぼすべての時間が生産的に使用されていることがわかりました。したがって、ここから改善するにはアルゴリズムの改善が必要だと思います。また、時間をもう少し節約するために、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

タイプミス:)私は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これらの組み合わせを介して機能反復。

  • find_periodブールのスライスを取り、期間を返します。

  • compute_count_arrayBoolsの各組み合わせのサブそれぞれ「2の累乗」のための仕事をしていません。

理論的に2^nは、u64の最大値、つまりを超えるまでオーバーフローは予想されませんn > 64。この制限は、s = [true、true、...、true]の高価なテストで解決できます。

悪いニュースは、n = 16で317を返すことですが、その理由はわかりません。n = 32の場合、10分で到達するかどうかもわかりません。なぜなら、Vec<bool>この種の計算には最適化されていないからです。

編集

  1. 64の制限を削除することに成功しましたn。現在、n最大使用整数より大きくなるまでクラッシュしません。

  2. 前のコードが317を返した理由を見つけましたn=32。期間の配列ではなく、期間のセットをカウントしていました。同じ要素を持つ3つの配列がありました。

    [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

2

C-16

16 cuzを超える値では失敗します。

repl.itで実行しているchromebookでこれがどれだけ速く実行されるかはわかりません。

質問を読みながら実装し、すべてのビット文字列を調べ、周期配列を計算し、すでにカウントされているかどうかを確認します。

#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 -それはのためerroringた16コードは、2つのように変化させたときにmallocsがあったmalloc(...int*))...**それぞれ16印刷Answer: 320予想通り、しかし32印刷Answer: 0(かなり素早く)。
ジョナサンアラン

@JonathanAllanが問題を修正し、bをint *にしました。
マルティセン

@JonathanAllan 32のことは、2 ** 32がintをオーバーフローさせるからです。また、メモリ不足になります。
マルティセン

@ThePirateBay私はiとmを長くしましたが、32を試してみるとセグメンテーション違反になります。
マルティセン

@マルティセン。使用可能なメモリ不足ではなく、割り当て/割り当て解除で何かを台無しにしたため、セグメンテーション違反が発生したようです。segfaultを取得しました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=16ます。99 92分n=32かかるので、私は9または10オフです。ルックアップテーブルにピリオドをキャッシュしてみたので、何度も何度も再計算する必要はありませんが、4GBマシンのメモリがすぐに不足します。


10分の1のオフにもかかわらず、コードは非常に見栄えが良いです。

@Lembik。ありがとう。私は改善を試みています:上記のコードは長さ1の部分文字列の期間を計算しますが、それは完全に不要です。それらを計算する必要がないことに加えて、期間の一意の配列を見つけるときに時間も節約されます。なぜなら、それらはすべて1つの要素より短いからです。
-nimi

@Lembik:長さ1の部分文字列を省略すると、n = 32で約7分節約できます。まだ長すぎます。
-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.