Periyot dizilerini say


11

periodDize herhangi bir parçayı o çıkıntısı yok sayarak kendisini eşleşmesi için bir dizenin en kısa sıfırdan kaymasıdır. Örneğin, bir abcabcabdönemi vardır 3. Kural olarak, böyle bir kayma yoksa, bir ipin uzunluğuna eşit bir periyodu olduğunu söylüyoruz. Dönemi Yani abcdeIS 5ve dönemi aolduğunu 1.

Daha resmi terimlerle, bir dizginin süresi Sminimumdur, i > 0böylece S[1,n-i] == S[i+1,n](indeksleme 1).

İki uzunlukta bir güç S dizisi için, iki uzunluğundaki tüm güç öneklerinin süresini hesaplayacağız. Örneğin, düşünün S = abcabcab. Hesaplayacağımız dönemler:

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

Aslında sadece nokta dizisini çıktılayacağız, yani [1, 2, 3, 3].

Verilen iki pozitif güç için n, tüm olası ikili dizeleri göz önünde bulundurun S. İkili bir dizenin basitçe 1s ve 0s dizisi olduğunu hatırlayın, böylece tam olarak 2^nbu dizeler (yani 2güç n) vardır. Her biri için bu dönem dizisini hesaplayabiliriz.

Zor olan ngirdi (iki güç) girdi olarak kod yazmak ve bu tür dizilerin kaç farklı olduğunu hesaplamaktır.

Cevapları n = 1, 2, 4, 8, 16, 32, 64, 128:

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

Farklı dönem dizilerinin tam kümesi n = 4:

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

Puan

Kodunuzu Ubuntu çalıştıran bilgisayarımda 10 dakika boyunca çalıştıracağım. Puanınız, nkodunuzun bu süre içinde sona erdiği en yüksek puandır . Beraberlik durumunda, en büyük eklemi tamamlayan cevap nkazanır. Zamanlamalarda 1 saniye içinde bir kravat olması durumunda, ilk cevap kazanır.

Diller ve kütüphaneler

İstediğiniz dil ve kitaplıkları kullanabilirsiniz. Lütfen mümkünse Linux'ta kodunuzu nasıl çalıştıracağınız / derleyeceğinize ilişkin tam bir açıklama ekleyin.`

Kodunuz yanıtları hesaplamalı ve örneğin yalnızca önceden hesaplanmış değerleri çıkarmamalıdır.

Öncü girişler

  • 2 dakika ve 21 saniye boyunca n = 128 yılında C # Peter Taylor tarafından
  • İsaacg tarafından Rust in n = 32 için 9 saniye

Bu kafamı incitti.
Henry

1
Sorun ilginç, ama yine de "önceden hesaplanmış" ve "aslında hesaplanmış" cevapları ayırt etmek için kullandığınız objektif kriterleri göremiyorum . Örneğin, kodumun nasıl çalıştığını anlayamazsanız, ancak çok büyük olanlar için doğru cevaplar verirse n, kabul eder misiniz? Sabit kodlama ile gerçek hesaplama arasındaki sınırın nerede olduğu iyi tanımlanmamıştır.


1
@ThePirateBay codegolf.meta.stackexchange.com/a/1063/9206 . Standart bir kuraldır.

2
@Cowsquack Dizenin ilk üç harfi hariç tümü abcab. Son 3 harf hariç hepsi abcab. Bunlar eşleşiyor ve daha az sayıda harfi kaldırmak uyuşmuyor.
isaacg

Yanıtlar:


9

C #, n = 128, yaklaşık 2:40

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

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

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

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

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

            return count;
        }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            return true;
        }

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

            return true;
        }

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

            throw new Exception("Unreachable");
        }

        class UnionSet
        {
            private int[] _Lookup;

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

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

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

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

N = 256 değerine genişletmek, n ​​= BigInteger128'in performansı n = 256 olsun, yeni fikirler olmadan geçmesi için muhtemelen çok fazla performansı öldüren maskeler için geçiş yapılmasını gerektirir .

Linux altında, ile derleyin mono-cscve çalıştırın mono.

Temel açıklama

Ben satır satır diseksiyon yapmayacağım, sadece kavramlara genel bir bakış.

Genel bir kural olarak, kaba kuvvet kombinatorik bir programda 2 50 elemanın sırasını yinelemekten mutluluk duyuyorum . Bu nedenle n = 128'e ulaşmak için her bit dizisini analiz etmeyen bir yaklaşım kullanmak gerekir. Yani bit dizelerinden dönem dizilerine doğru ilerlemek yerine, geriye doğru çalışıyorum: bir dönem dizisi verildiğinde, bunu gerçekleştiren bir bit dizisi var mı? N = 2 x için , 2 x (x + 1) / 2 periyot dizisinin (2 2 x bit dizisine kıyasla ) kolay bir üst sınırı vardır .

Bazı argümanlar dize periyodiklik lemmasını kullanır :

Izin vermek pve quzunluk dizesinin iki periyodu olsun n. Eğer p + q ≤ n + gcd(p, q)öyleyse gcd(p, q), dizenin bir dönemidir.

Wlog Düşünülen tüm bit dizgilerinin ile başladığını varsayacağım 0.

2 i uzunluğunun önekinin periyodu ( her zaman) olan bir dönem sırası verildiğinde , aşağıdakilerin olası değerleri hakkında bazı kolay gözlemler vardır :[p1 p2 ... pk]pip0 = 1pk+1

  • pk+1 ≥ pkçünkü bir dizginin Speriyodu da herhangi bir önekinin periyodudur S.

  • pk+1 = pk her zaman olası bir uzantıdır: sadece aynı ilkel dizeyi iki kat daha fazla karakter için tekrarlayın.

  • 2k < pk+1 ≤ 2k+1her zaman olası bir uzantıdır. Bunun gösterilmesi yeterlidir, çünkü bir aperiodic uzunluk dizisi bir aperiodic uzunluk dizisine uzatılabilirpk+1 = 2k+1LL+1 , ilk harfi olmayan herhangi bir harf eklenerek aperiodik bir .

    SxSüresi 2 k olan bir dize alın ve 2 k + 1 uzunluğunda olan dizeyi düşünün . Açıkça sahip bir dönem 2 k + 1. Diyelim ki süresi daha küçük.pkSxySSxySq

    Öyleyse periyodiklik ile lemma da bir dönemdir ve en büyük bölen argümanlarına eşit veya daha küçük olduğu ve en küçük dönem olduğu için , 2 k + 1'lik uygun bir faktör olmamız gerekir . Bölüm 2 olamaz, biz var .2k+1 + q ≤ 2k+1+1 ≤ 2k+1 + gcd(2k+1, q)gcd(2k+1, q)SxySqqq ≤ (2k+1)/3

    Şimdi bir dönem olduğu için bir dönem olmalı . Ama dönemi olduğunu . İki vakamız var:q ≤ 2kSxySSxSxpk

    1. gcd(pk, q) = pkveya eşdeğer olarak tam olarak bölünür .pkq
    2. pk + q > 2k + gcd(pk, q) böylece periyodik lemma daha küçük bir dönemi zorlamaz.

    İlk olarak ikinci vakayı düşünün. , dönemi olarak tanımlanması ile çelişiyor . Bu nedenle , bir faktör olan sonuca zorlanıyoruz .pk > 2k + gcd(pk, q) - q ≥ 2k+1 - q ≥ 2k+1 - (2k+1)/3 ≥ 2qpkSxpkq .

    Fakat qbir dönemdir Sxve süresidir , uzunluğunun önek adildir uzunluğunun önek kopyaları biz görüyoruz, böylece de bir dönemdir .pkSxqq/pkpkpkSxyS

    Bu nedenle süresi SxySya bir ya da 2 k + 1. Ama iki seçeneğimiz var ! En fazla bir seçim dönemi verir , bu yüzden en az biri dönem 2 k +1 verir. QED.pkyypk

  • Periyodik lemma, kalan olası uzantıların bazılarını reddetmemize izin verir.

  • Hızlı kabul veya hızlı reddetme testini geçemeyen uzantıların yapıcı olarak test edilmesi gerekir.

Bir periyot dizisi verilen bir bit dizisinin oluşturulması esasen bir tatmin edilebilirlik problemidir, ancak çok fazla yapıya sahiptir. Her önek periyodunun ima ettiği basit eşitlik kısıtlamaları vardır , bu yüzden bitleri bağımsız kümeler halinde birleştirmek için bir birleşim kümesi veri yapısı kullanırım. Bu, n = 64 ile başa çıkmak için yeterliydi, ancak n = 128 için daha ileri gitmek gerekiyordu. İki yararlı tartışma çizgisi kullanıyorum:2k - pk

  1. Uzunluğunun öneki ise Molduğu ve boyunun önek süreye sahip sonra uzunluğunun önek içinde sona ermelidir . Bu, aksi takdirde en bağımsız kümeleri en uygun olan durumlarda en güçlü olanıdır.01M-1L > MLL1M
  2. Uzunluğunun öneki ise Molduğu ve boyunun önek süreye sahip olan ve uçları o zaman gerçeği sonunda bir zorunluluktur . Bu, dönem sekansı çok sayıda ile başladığında, en uçta en güçlü olanıdır.0ML > ML - dd < M0d10d

Kümeyi ilk bitle (sıfır olduğu varsayılır) bir olacak şekilde zorlayarak derhal bir çelişki almazsak, kuvvetlenmemiş kümeler için olası değerler üzerinde kuvvet (bazı mikro optimizasyonlar ile) şiddetleniriz. Siparişin azalan sayıda olduğuna dikkat edin, çünkü ith bit bir ise, dönem olamaz ive kümelenme tarafından zaten zorlananlardan daha kısa sürelerden kaçınmak isteriz. Aşağı inmek, geçerli bir ödevi erken bulma şansını artırır.


Bu gerçekten büyük bir başarı! Ben çok etkilendim.

@Lembik, kodu basitleştirdim ve optimize ettim ve çalışma süresini n = 128 için yaklaşık üçte bir oranında azalttım.
Peter Taylor

1
Bunun için hangi algoritmayı tasarladığınızı bilmek isterim. Kodunuzda çok az mantık var ve çok akıllı bir şey yapıyor olmalı.

7

Dizüstü bilgisayarımda pas, 32, 10s 11s 29s

Komut satırı argümanı olarak bitsize ile çağırın.

Akıllı teknikler: bit dizelerini doğrudan sayılar olarak temsil edin, döngüleri kontrol etmek için bittwiddling kullanın. Bit dizelerinin yalnızca ilk yarısını, 0 ile başlayanları ara. Son pozisyon için her olasılık zaten meydana gelmişse, onu aramıyorum.

Daha akıllıca şeyler:

Her bloğu tekilleştirmek için (bitlerin ilk yarısının aynı olduğu dizeler) Son döngü uzunluklarının hash gerektirmediği için bir hashset'ten çok daha hızlı olan bir bitvector kullanıyorum.

Ayrıca, döngü kontrolünün ilk adımlarını atlıyorum, çünkü son döngünün ikinci ila son döngüden daha kısa olamayacağını biliyorum.

Çok sayıda profillemeden sonra, hemen hemen tüm zamanların verimli bir şekilde kullanıldığını söyleyebilirim, bence buradan geliştirmek için algoritmik iyileştirmeler gerekecek. Biraz daha fazla zaman kazanmak için 32 bit tam sayılara geçtim.

//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'nizi koyun

Bunu çalıştırmak istiyorsanız, bunu klonlayın: github.com/isaacg1/cycle sonra Cargo build --releaseinşa etmek, sonra Cargo run --release 32çalıştırmak için.


Görünüşe göre eprintln, 0.16.0'dan sonra bir pas sürümüne ihtiyaç duyuyor. Println olarak değiştirirsem çalışır.

Bu cevap çok etkileyici. timedizüstü bilgisayarımda 27 kullanıcı saniye veriyor.

@Lembik neden bu kadar eski bir pas sürümündesin? Rust 1.0 yıllar önce çıktı.
isaacg

Yazım hatası :) 1.16.0 demek istedim. blog.rust-lang.org/2017/03/16/Rust-1.16.html

Pas yeni başlayanlar için, kodunuzu kargo kullanarak nasıl derleyeceğinizi yazmayı düşünür müsünüz?

4

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

Çevrimiçi deneyin!

Derleme: rustc -O <name>.rs

Dize bir Bool vektörü olarak uygulanır.

  • nextKombinasyonları ile işlev yineleme;

  • find_periodBOOL dilim alır ve süresini verir;

  • compute_count_arrayBool değerlerden oluşan her kombinasyonlarının altsekansının her "iki güç" için işi yapar.

Teorik olarak, 2^nu64 maksimum değerini aşana kadar taşma beklenmez , yanin > 64 . Bu sınır, s = [true, true, ..., true] üzerinde pahalı bir testle aşılabilir.

Kötü haber şu: n = 16 için 317 döndürüyor, ama nedenini bilmiyorum. Vec<bool>Bu tür bir hesaplama için optimize edilmediğinden , n = 32 için bunu on dakika içinde yapıp yapmayacağını bilmiyorum .

DÜZENLE

  1. İçin 64 sınırını kaldırmayı başardım n. Şimdi, nmaksimum kullanım tamsayısından daha büyük olana kadar çökmeyecek.

  2. Önceki kod neden 317 için döndü bulundu n=32. Dönem dizilerini sayıyordum , dönem dizilerini değil . Aynı unsurlara sahip üç dizi vardı:

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

Şimdi çalışıyor. Hala yavaş ama işe yarıyor.


İşte n = 16 bpaste.net/show/3664e25ebc01 için 320 tane .

1
@Lembik Listeniz sayesinde 317 için açıklamayı buldum.
jferard

2

C - 16

16 cuz taşma değerinden büyük değerlerde başarısız olur.

Bu repl.it üzerinde çalışan bir chromebook cuz im ne kadar hızlı çalıştığı hakkında hiçbir fikrim yok.

Soruyu okuduğu gibi uygular, tüm bit dizelerinden geçer, dönem dizilerini hesaplar ve önceden sayılmış olup olmadıklarını kontrol eder.

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

Sadece gcc vb. İle derleyin.


Bilginize - O için erroring edilmiş 16kod iki şekilde değiştirildiği zaman sonra mallocs idi malloc(...int*))ve ...**sırasıyla 16baskılı Answer: 320beklendiği gibi, ancak 32basılmış Answer: 0(oldukça hızlı bir şekilde ve benzeri).
Jonathan Allan

@JonathanAllan şeyleri düzeltti, b'yi bir int * yaptı.
Maltysen

@JonathanAllan 32 şey çünkü 2 ** 32 int taşar. Ayrıca prolly bellek tükenecek.
Maltysen

@ThePirateBay i ve m longs yaptı ve sadece 32 denediğimde segfaults. Repl.it/JwJl/2 Sanırım bellek bitti.
Maltysen

@Maltysen. Görünen o ki, bellek yetersizliğinden ziyade tahsis / dağıtmada bir şeyleri karıştırdığınız için segfaults gibi görünüyor. Segfault aldım n = 8ama sonuç yazdırıldıktan sonra, yığının bozuk olduğu anlamına gelir. Muhtemelen tahsis edilmiş bellek bloklarının ötesinde bir yerdesiniz.

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

İle derleyin ghc -O2.Çevrimiçi deneyin!

Benim 6 yaşındaki dizüstü bilgisayar donanımında 0.1sn daha az çalışır n=16. 99 92 dk n=32alır , bu yüzden faktör 9 veya 10 kapalı. Bir arama tablosunda dönemleri önbelleğe almayı denedim, bu yüzden onları tekrar tekrar hesaplamak zorunda değilim, ancak bu hızlı bir şekilde 4GB'lık makinemde bellek tükeniyor.


10 faktörü olmasına rağmen, kodunuz çok iyi görünüyor.

@Lembik. Teşekkürler. Ben sadece bir iyileştirme çalışıyorum: yukarıdaki kod uzunluğu 1 alt dizeleri için dönemleri hesaplar, ama bu tamamen gereksiz. Bunları hesaplamak zorunda kalmanın yanı sıra, benzersiz dönem dizileri bulurken de zaman kazandırır, çünkü hepsi bir unsur daha kısadır.
nimi

@Lembik: uzunluk 1 alt dizelerini atlamak n = 32 için yaklaşık 7 dakika tasarruf sağlar. Hala çok uzun.
nimi

Dönemin hesaplanmasında yardımcı olabilecek hızlı bir doğrusal algoritma vardır.

Gerçekten 2 ^ 16 boyutunda bir arama tablosu oluşturamaz mısınız? Çok büyük görünmüyor.

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 neden bu kadar uzun sürüyor
sadece ASCII sadece

Yarısını atlayabileceğimi biliyorum ama IDK nasıl: /
sadece ASCII sadece

Kodunuz benim için yalnızca "Yok" çıktı gibi görünüyor. Nasıl koşuyorsun? osboxes@osboxes:~/python$ python ascii_user.py 16 None

bok üzgünüm bu aslında ne ben çalıştırmak değil
ASCII-sadece

@Lembik şimdi düzeltildi
sadece ASCII-

1

[C ++], 32,4 dakika

#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;
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.