Yalnızca bir kez ortaya bir dize karakterleri bulma

6 Cevap php

Ben belirli bir Sudoku bulmaca çözmek için PHP bir algoritma yazıyorum. 9x9 tahtada her karo için Square sınıf ve {bir matris olan bir Sudoku sınıfı: ben iki sınıfları ile biraz nesne yönelimli uygulama kurdum [(0)]} kurulu temsil etmek var.

Ben kullanıyorum algoritmasının uygulanması üçlü katmanlı yaklaşımın bir tür. En temel bulmaca (ama en verimli) sadece çözecek ilk adım, sadece Forumun ilk kurulum dayalı tek bir değer alabilir ve dinlenme ona göre kısıtlamaları ayarlamak için herhangi kareleri doldurmak için faili meçhul kareler.

Genellikle, "sürekli yayılma" Bu süreç tamamen tahta çözmez, ama oldukça büyük bir yığın çözmek gelmez. Bu, her faili meçhul karenin "olası" değerler için her birimi (ya da tüm benzersiz numara atamaları olmalıdır 9 kareler, örneğin satır ya da sütun) ayrıştırmak in ikinci kademe sonra başlayacak. Olası değerler bu liste Square sınıfında bir dize olarak temsil edilir:

class Square {

private $name;                // 00, 01, 02, ... , 86, 87, 88
private $peers;               // All squares in same row, col, and box
private $number;              // Assigned value (0 if not assigned)
private $possibles;           // String of possible numbers (1-9)

public function __construct($name, $p = 0) {
  $this->name = $name;
  $this->setNumber($p);
  if ($p == 0) {
    $this->possibles = "123456789";
  }
}

// ... other functions

Bir birim çözülmemiş kareler bir bütün dizi (yukarıda ikinci kademe açıklandığı gibi) göz önüne alındığında, ikinci kademe tek bir dize içine "olabileceklerin" bütün dizeleri birleştirmek olacaktır. Değerler kendilerini tekrar yok - o zaman herhangi bir benzersiz bir karakter değerleri için o tek dize yoluyla arayacaktır. Bu kareler birimi içinde, o belirli değer alabilir tek bir kare, orada olduğunu gösterecektir.

Benim soru: Bu ikinci kademe uygulanması için, nasıl bir birimindeki tüm olası değerleri bu dizeyi ayrıştırmak ve kolayca benzersiz bir değer (ler) tespit? Ben her endeks sayıları 1-9 ile temsil edilir bir dizi oluşturmak olabileceğini biliyorum, ve ben her tekrar dizi tarayın, sonra ben buluyorum bu sayının her olası-değeri için 1 ile ilgili dizin değerini artırmak olabilir 1 değerleri, ama bu her birim için bir dizinin iki lineer taramalar gerektiren, son derece verimsiz görünüyor, ve bir Sudoku bulmaca 27 adet vardır.

6 Cevap

Bu biraz oldukça verimli olabilir bu yüzden zaten "son derece verimsiz" olarak dışladı ne gibi, ancak yerleşik fonksiyonları ile:

$all_possibilities = "1234567891234";
$unique = array();
foreach (count_chars($all_possibilities, 1) as $c => $occurrences) {
  if ($occurrences == 1)
    $unique[] = chr($c);
}
print join("", $unique) . "\n";

Baskılar: "56789"

XOR gibi VE, VEYA ikili işlemler string işlemleri çok daha hızlı olma eğilimindedir, çünkü, bunun yerine "possibles" temsil eden bir ikili sayı kullanarak düşünün.

Örneğin "2" ve "3" bir kare için mümkün olmadığını, o kare için olanakları temsil ikili sayı 000000110 kullanın.

Burada tekil bulabildiğim nasıl:

$seenonce = 0;
$seenmore = 0;
foreach(all_possibles_for_this_unit as $possibles) {
    $seenmore |= ($possibles & $seenonce);
    $seenonce |= $possibles;
}
$seenonce ^= $seenmore;
if ($seenonce) {
    //something was seen once - now it must be located
}

Ben bu yöntem aslında daha hızlı çalışacaktır eğer emin değilim ama içine bakarak değer.

 function singletonsInString($instring) {

    $results = array();

    for($i = 1; $i < 10; $i++) {

        $first_pos = strpos($instring, str($i));
        $last_pos = strrpos($instring, str($i));

        if ( $first_pos !== FALSE and $first_pos == $last_pos ) 
            $results[] = $i;

    }

    return $results;

 }

Bu size her tekil vereceğim. O dizideki bir sayının ilk ve son pozisyonları alın ve onlar maç ve hem YANLIŞ (başında bir tek doğru var durumda sıkı bir karşılaştırma) değilse o dizide sadece tek bir numara var.

Burada süper hız süper hakkında endişeli iseniz, muhtemelen bu döngünün içini değiştirebilirsiniz

 $istr = str($i);
 if ( ($first = strpos($instring, $istr)) !== FALSE 
       and $first == $strrpos($instring, $istr) ) $results[] = $i;

hesaplamaların bir en az sayısı için. Peki, PHP'nin strpos varsayarak bildiğim kadarıyla mantıksız değil bu şeyler hakkında gitmek için en iyi yoldur.

Ben Sudoku çözme ile aldatmasın son kez, ben "Çalıştır" denilen üçüncü bir sınıf vardı. A Run örneği, her satır, sütun ve 3x3 kare için oluşturulur. Her kare onunla ilişkili üç çalışır var. Run sınıfı henüz vadede içinde yer değil sayı kümesi içerir. Kurulu Çözme sonra iteratif her meydanında setleri kesişen içerir. Bu çoğu orta kurullarının% 80 ve en sert kurullarının% 60 önemser. Eğer herhangi bir değişiklik ile tüm tahta geçtikten sonra, daha yüksek seviyeli mantık taşıyabilirsiniz. Sizin yüksek düzey mantığı bir kareyi doldurur her zaman, tekrar kareler ile çalıştırın.

Bu kurulumu hakkında güzel bir şey kolayca çözücüye türevlerini ekleyebilir. Eğer iki çapraz de benzersiz varyant kullandığını söylüyor. Sadece bu 18 kareler 4'üncü çalıştırmak ekleyin.

Ben ne yapardım, başka bir cevap olarak önerilen gerçek değerlerini saklamak için ikili bit kullanmak aslında. Bu verimli denetimlerini yapmak ve genel olarak daha matematiksel (= verimli ve daha kısa) solüsyonu (sadece benim izlenimim, ben bu araştırılmış değil) için Sudoku kendisini ödünç olabilir sağlar.

Temelde, değil hane ile, ancak 2 güçlerle kareler sayıları temsil

"1" = 2^0 = 1 =  000000001
"2" = 2^1 = 2 =  000000010
"3" = 2^2 = 4 =  000000100
"4" = 2^3 = 8 =  000001000
... etc up to 
"9" = 2^8 = 256= 100000000

Bu şekilde, sadece bu gibi numaraları 3x3 veya bir satır veya sudoku diğer herhangi bir alt kümesi eksik ne olduğunu öğrenmek için tek kareler içeriklerini ekleyebilirsiniz:

// shows the possibles for 3x3 square number 1 (00-22)
$sum=0;
for ($i=0; $i< 3; $i++)
  for ($j=0; $j < 3; $j++)
         $sum += $square["${i}${j}"]->number

$possibles = $sum ^ 511  // ^ stands for bitwise XOR and 511 is binary 11111111

Şimdi $ possibles bu meydanda mümkündür ve bunun gibi, onları bir arada maç diğer kareler için sonuçları ile bitsel işlemleri yapabilirsiniz basamak bit konumunda "1" içerir:

örn. diyelim:

$possibles1 = 146 // is binary 100100101, 
                 //indicating that this row or 3x3 square has place for "9", "6", "3" and "1"
$possibles2 = 7 //   is binary 000000111, indicating it has place for "3", "2" and "1".

// so:
$possibles1 & $possibles2 
// bitwise AND, will show binary 101 saying that "3" and "1" is unfilled in both bloces
$possibles1 | $possibles2 
// bitwise OR will give that in total it is possible to use "9", "6", "3", "2" and "1" in those two squares together

Burada yerleşik oldukça hızlı olması gereken işlevler sadece PHP kullanarak bir yoldur.

function getUniques($sNumbers)
{
    return join(array_keys(array_count_values(str_split($sNumbers)),1));
}

echo getUniques("1234567891234"); // return 56789;