Hitung array periode


11

The periodstring adalah terpendek non-zero pergeseran sehingga string cocok itu sendiri, mengabaikan semua bagian yang overhang. Jadi misalnya, abcabcabada titik 3. Dengan konvensi kita katakan bahwa jika tidak ada pergeseran seperti itu maka string memiliki periode yang sama dengan panjangnya. Jadi periode abcdeis 5dan periode ais 1.

Dalam istilah yang lebih formal, periode string Sadalah minimum i > 0sehingga S[1,n-i] == S[i+1,n](pengindeksan dari 1).

Untuk string S yang diberi kekuatan dua panjang, kami akan menghitung periode semua awalan kekuatan dua panjangnya. Sebagai contoh, pertimbangkan S = abcabcab. Periode yang akan kami hitung adalah:

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

Kami sebenarnya hanya akan menampilkan array periode, yaitu [1, 2, 3, 3].

Untuk kekuatan positif yang diberikan dari dua n, pertimbangkan semua string biner yang mungkin S. Ingat bahwa string biner hanyalah string 1s dan 0s sehingga ada 2^nstring yang persis seperti itu (yaitu 2untuk kekuatan n). Untuk masing-masing kita dapat menghitung array periode ini.

Tantangannya adalah untuk menulis kode yang mengambil n(kekuatan dua) sebagai input dan menghitung berapa banyak array yang berbeda.

Jawabannya n = 1, 2, 4, 8, 16, 32, 64, 128adalah:

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

Rangkaian lengkap periode berbeda untuk n = 4:

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

Skor

Saya akan menjalankan kode Anda di komputer saya yang menjalankan Ubuntu selama 10 menit. Skor Anda adalah yang terbesar nuntuk mana kode Anda berakhir pada waktu itu. Dalam kasus seri, jawaban yang melengkapi nkemenangan tercepat bersama . Jika ada ikatan dalam waktu 1 detik pada timing, jawaban pertama yang diposting menang.

Bahasa dan perpustakaan

Anda dapat menggunakan bahasa dan perpustakaan yang tersedia yang Anda suka. Harap sertakan penjelasan lengkap tentang cara menjalankan / kompilasi kode Anda di Linux jika memungkinkan. `

Kode Anda sebenarnya harus menghitung jawaban dan tidak, misalnya, hanya menampilkan nilai yang telah dihitung.

Entri terkemuka

  • 2 menit dan 21 detik untuk n = 128 dalam C # oleh Peter Taylor
  • 9 detik untuk n = 32 di Rust oleh isaacg

Ini membuat kepala saya sakit.
Henry

1
Tantangannya adalah menarik, tapi aku masih tidak bisa melihat criterium tujuan yang Anda gunakan untuk membedakan antara "Precomputed" dan "benar-benar dihitung" jawaban. Jika Anda, misalnya, tidak dapat memahami cara kerja kode saya, tetapi memberikan jawaban yang benar untuk ukuran besar n, apakah Anda akan menerimanya? Tidak didefinisikan dengan baik di mana batas antara hardcoding dan komputasi aktual.


1
@ThePirateBay codegolf.meta.stackexchange.com/a/1063/9206 . Itu aturan standar.

2
@Cowsquack Semua kecuali tiga huruf pertama dari string abcab. Semua kecuali 3 huruf terakhir adalah abcab. Ini cocok, dan menghapus sejumlah kecil huruf tidak cocok.
isaacg

Jawaban:


9

C #, n = 128 dalam sekitar 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;
            }
        }
    }
}

Memperluas ke n = 256 akan membutuhkan peralihan ke BigIntegeruntuk topeng, yang mungkin membunuh kinerja terlalu banyak untuk n = 128 untuk dilewati tanpa ide-ide baru, apalagi n = 256.

Di Linux, kompilasi dengan mono-cscdan jalankan dengan mono.

Penjelasan dasar

Saya tidak akan melakukan pembedahan baris demi baris, hanya tinjauan umum konsep.

Sebagai aturan praktis, saya senang untuk mengulangi urutan 2 50 elemen dalam program kombinatorik brute-force. Untuk sampai ke n = 128 oleh karena itu perlu menggunakan pendekatan yang tidak menganalisis setiap bitstring. Jadi daripada bekerja maju dari string bit ke urutan periode, saya bekerja mundur: diberi urutan periode, apakah ada bitstring yang menyadarinya? Untuk n = 2 x ada batas atas yang mudah yaitu 2 x (x + 1) / 2 urutan periode (vs 2 2 x bitstring).

Beberapa argumen menggunakan string periodisitas string :

Biarkan pdan qmenjadi dua periode string panjang n. Jika p + q ≤ n + gcd(p, q)kemudian gcd(p, q)juga merupakan periode dari string.

Wlog Saya akan menganggap bahwa semua bitstring yang dipertimbangkan mulai dengan 0.

Diberi urutan periode di mana periode awalan panjang 2 i ( selalu), ada beberapa pengamatan mudah tentang nilai-nilai yang mungkin dari :[p1 p2 ... pk]pip0 = 1pk+1

  • pk+1 ≥ pkkarena periode string Sjuga merupakan periode awalan S.

  • pk+1 = pk selalu merupakan ekstensi yang memungkinkan: cukup ulangi string primitif yang sama untuk karakter dua kali lebih banyak.

  • 2k < pk+1 ≤ 2k+1selalu ada kemungkinan ekstensi. Cukup untuk menunjukkan ini karena string panjang aperiodik dapat diperluas ke string panjang aperiodik dengan menambahkan huruf apa pun yang bukan huruf pertama.pk+1 = 2k+1LL+1

    Ambil string Sxdengan panjang 2 k yang periodenya dan perhatikan string dengan panjang 2 k + 1 . Jelas memiliki sebuah periode 2 k 1. Misalkan periodenya lebih kecil.pkSxySSxySq

    Maka demikian oleh lemma periodisitas juga merupakan periode , dan karena pembagi terbesar kurang dari atau sama dengan argumennya dan merupakan periode terkecil, kita perlu menjadi faktor yang tepat yaitu 2 k +1. Karena hasil bagi tidak boleh 2, kami punya .2k+1 + q ≤ 2k+1+1 ≤ 2k+1 + gcd(2k+1, q)gcd(2k+1, q)SxySqqq ≤ (2k+1)/3

    Sekarang, karena adalah periode maka harus periode . Tapi yang periode ini . Kami memiliki dua kasus:q ≤ 2kSxySSxSxpk

    1. gcd(pk, q) = pk, atau secara ekivalen membagi menjadi .pkq
    2. pk + q > 2k + gcd(pk, q) sehingga lemma periodisitas tidak memaksa periode yang lebih kecil.

    Pertimbangkan dulu kasus kedua. , bertentangan dengan definisi sebagai periode . Oleh karena itu kita dipaksa ke kesimpulan yang merupakan faktor .pk > 2k + gcd(pk, q) - q ≥ 2k+1 - q ≥ 2k+1 - (2k+1)/3 ≥ 2qpkSxpkq

    Tetapi karena qmerupakan periode Sxdan merupakan periode , awalan panjang hanyalah salinan awalan panjang , jadi kita melihat bahwa itu juga merupakan periode .pkSxqq/pkpkpkSxyS

    Oleh karena itu jangka waktunya SxySadalah atau 2 k +1. Tapi kami punya dua pilihan untuk ! Paling banyak satu pilihan akan memberikan periode , jadi setidaknya satu akan memberikan periode 2 k +1. QED.pkyypk

  • Lemma periodisitas memungkinkan kami untuk menolak beberapa ekstensi yang tersisa.

  • Setiap ekstensi yang belum lulus tes penerimaan cepat atau penolakan cepat harus diuji secara konstruktif.

Konstruksi bitstring yang diberikan urutan periode pada dasarnya adalah masalah kepuasan, tetapi memiliki banyak struktur. Ada kendala kesetaraan sederhana yang tersirat oleh setiap periode awalan, jadi saya menggunakan struktur data kumpulan serikat untuk menggabungkan bit ke dalam kelompok independen. Ini cukup untuk mengatasi n = 64, tetapi untuk n = 128 itu perlu untuk melangkah lebih jauh. Saya menggunakan dua argumen yang bermanfaat:2k - pk

  1. Jika awalan panjang Madalah dan awalan panjang memiliki periode maka awalan panjang harus diakhiri . Ini paling kuat justru dalam kasus-kasus yang seharusnya memiliki kelompok paling independen, yang nyaman.01M-1L > MLL1M
  2. Jika awalan panjang Madalah dan awalan panjang memiliki periode dengan dan berakhir di maka itu sebenarnya harus diakhiri dengan . Ini paling kuat pada ekstrim yang berlawanan, ketika urutan periode dimulai dengan banyak yang.0ML > ML - dd < M0d10d

Jika kita tidak mendapatkan kontradiksi langsung dengan memaksa cluster dengan bit pertama (diasumsikan nol) menjadi satu maka kita brute force (dengan beberapa optimasi mikro) atas nilai-nilai yang mungkin untuk cluster paksa. Perhatikan bahwa urutannya dalam jumlah yang menurun karena jika bit ike-1 adalah periode maka periode tidak dapat idan kami ingin menghindari periode yang lebih pendek daripada yang sudah diberlakukan oleh pengelompokan. Turun meningkatkan kemungkinan menemukan tugas yang valid lebih awal.


Ini pencapaian yang luar biasa! Saya sangat terkesan.

@ Lembik, saya telah menyederhanakan dan mengoptimalkan kode dan mengurangi runtime untuk n = 128 sekitar sepertiga.
Peter Taylor

1
Saya ingin tahu algoritma apa yang Anda rancang untuk ini. Kode Anda memiliki logika yang sangat sedikit di dalamnya dan harus melakukan sesuatu yang sangat pintar.

7

Rust, 32, 10s 11s 29s di laptop saya

Sebut dengan bitsize sebagai argumen baris perintah.

Teknik pintar: mewakili bitstring langsung sebagai angka, gunakan bittwiddling untuk memeriksa siklus. Hanya mencari paruh pertama bitstring, yang dimulai dengan 0, karena array periode bitstring dan kebalikannya (0s ditukar dengan 1s) identik. Jika setiap kemungkinan untuk posisi akhir telah terjadi, saya tidak mencarinya.

Lebih banyak hal pintar:

Untuk mendupuplikasi setiap blok (string di mana bagian pertama bitnya sama) Saya menggunakan bitvector, yang jauh lebih cepat daripada hashset, karena panjang siklus akhir tidak perlu hashing.

Juga, saya melewatkan langkah pertama pemeriksaan siklus, karena saya tahu bahwa siklus akhir tidak bisa lebih pendek dari siklus kedua hingga terakhir.

Setelah banyak profil, sekarang saya dapat mengatakan bahwa hampir semua waktu digunakan secara produktif, jadi perbaikan algoritmik akan diperlukan untuk meningkatkan dari sini, saya pikir. Saya juga beralih ke integer 32 bit untuk menghemat lebih banyak waktu.

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

Masukkan bit-vec = "0.4.4"Cargo.toml Anda

Jika Anda ingin menjalankan ini, tiruan ini: github.com/isaacg1/cycle lalu Cargo build --releaseuntuk membangun, lalu Cargo run --release 32jalankan.


Sepertinya eprintln membutuhkan versi karat setelah 0.16.0. Ini berfungsi jika saya mengubahnya ke println.

Jawaban ini sangat mengesankan. timememberikannya 27 detik pengguna di laptop saya.

@ Lembik mengapa kamu dalam versi karat yang lama? Rust 1.0 keluar tahun lalu.
isaacg


Untuk pemula yang baru berkarat, maukah Anda menguraikan dengan tepat cara mengkompilasi kode menggunakan kargo?

4

Rust , 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
}

Cobalah online!

Kompilasi: rustc -O <name>.rs

String diimplementasikan sebagai vektor Bool.

  • The nextfungsi iterate melalui kombinasi;

  • The find_periodmengambil sepotong Bool dan mengembalikan periode;

  • The compute_count_arraymelakukan pekerjaan untuk setiap "kekuatan dua" subsequence setiap kombinasi dari bools.

Secara teoritis, tidak ada overflow yang diharapkan sampai 2^nmelebihi nilai maksimum u64, yaitu n > 64. Batas ini dapat dijangkau dengan tes mahal pada s = [true, true, ..., true].

Berita buruknya adalah: mengembalikan 317 untuk n = 16, tapi saya tidak tahu mengapa. Saya juga tidak tahu apakah itu akan membuatnya dalam sepuluh menit untuk n = 32, karena Vec<bool>tidak dioptimalkan untuk jenis komputasi ini.

EDIT

  1. Saya telah berhasil menghapus batas 64 untuk n. Sekarang, itu tidak akan crash sampai nlebih besar dari integer usize maks.

  2. Saya menemukan mengapa kode sebelumnya mengembalikan 317 untuk n=32. Saya menghitung set titik dan bukan susunan titik. Ada tiga array dengan elemen yang sama:

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

Sekarang berhasil. Masih lambat tapi berhasil.


Ini semua 320 untuk n = 16 bpaste.net/show/3664e25ebc01 .

1
@Lembik Saya menemukan penjelasan untuk 317 berkat daftar Anda.
jferard

2

C - 16

Gagal pada nilai yang lebih besar dari 16 cuz overflow.

Saya tidak tahu seberapa cepat ini berjalan karena pada chromebook menjalankannya di repl.it.

Cukup terapkan pertanyaan saat dibaca, telusuri semua string bit, hitung array periode, dan periksa apakah sudah dihitung.

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

Kompilasi saja dengan gcc, dll.


FYI - Kesalahan 16saat itu ketika kode diubah sehingga kedua mallocs malloc(...int*))dan ...**masing - masing 16dicetak Answer: 320seperti yang diharapkan, namun 32dicetak Answer: 0(dan cukup cepat).
Jonathan Allan

@ Jonathan Allan memperbaiki barang-barang itu, baru saja membuatnya menjadi int *.
Maltysen

@ Jonathan Allan the 32 thing is cuz 2 ** 32 meluap int. Saya juga akan kehabisan memori.
Maltysen

@ThePirateBay saya membuat saya dan saya rindu, dan itu hanya kesalahan ketika saya mencoba 32. repl.it/JwJl/2 Saya kira itu kehabisan memori.
Maltysen

@Maltysen. Tampaknya itu segfaults karena Anda mengacaukan sesuatu dalam alokasi / deallokasi daripada kekurangan memori yang tersedia. Saya mendapat segfault untuk n = 8tetapi setelah hasilnya dicetak, yang berarti bahwa tumpukan rusak. Mungkin Anda berada di suatu tempat di luar blok memori yang dialokasikan.

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

Kompilasi dengan ghc -O2. Cobalah online!

Berjalan dalam waktu kurang dari 0,1 detik pada perangkat keras laptop saya yang berumur 6 tahun untuk n=16. n=32membutuhkan 99 92 menit, jadi saya faktor 9 atau 10 off. Saya sudah mencoba caching periode dalam tabel pencarian jadi saya tidak perlu menghitung ulang mereka berulang-ulang, tetapi ini dengan cepat kehabisan memori pada mesin 4GB saya.


Meskipun faktor 10, kode Anda sangat tampan.

@Lembik. Terima kasih. Saya hanya mencoba perbaikan: kode di atas menghitung periode untuk substring dengan panjang 1, tapi itu sama sekali tidak perlu. Selain tidak harus menghitungnya, ia juga menghemat waktu ketika menemukan array periode yang unik, karena mereka semua adalah satu elemen yang lebih pendek.
nimi

@ Lembik: menghilangkan panjang 1 substring menghemat sekitar 7 menit untuk n = 32. Masih terlalu lama.
nimi

Ada algoritma linear cepat untuk menghitung periode yang mungkin membantu.

Bisakah Anda benar-benar tidak membangun tabel melihat ukuran 2 ^ 16? Itu tidak terlihat terlalu besar.

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

: | mengapa 32 harus begitu lama
ASCII

Saya tahu saya bisa melewati setengah dari mereka tetapi IDK bagaimana: /
ASCII-only

Kode Anda sepertinya hanya menghasilkan "Tidak Ada" untuk saya. Bagaimana Anda menjalankannya? osboxes@osboxes:~/python$ python ascii_user.py 16 None

omong kosong maaf ini sebenarnya bukan apa yang saya jalankan
ASCII-hanya

@Lembik diperbaiki sekarang
ASCII

1

[C ++], 32, 4 menit

#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;
}
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.