已知一個陣列,求出這個陣列的所有子集
之前看到這樣一個這樣的問題,第一反應是排列組合,但後來一想不容易實現。閒著無事,不如把這道題當做一個練習題練習一下,也發現 php 原生函式組合起來可以實現好多東西,就是不太容易組合使用,這裡記錄一下解題方法
1. 思考從何處下手去解決子集
我們都知道一個含有 n 個元素的集合,它的子集共有 2^n 個,其中必有一個為空集,這裡我們不考慮空集,只需求出它的真子集。
對於集合中的每一個元素,都可以被選,也可以不被選,這也就解釋了 2^n 從何而來,此處,我們就利用這個思路來處理這個問題。我們用 0 表示不選擇元素,用 1 表示選擇元素,故我們可以生成 0/1 序列來解決子集問題。
我們可以利用二進位制實現 0/1 序列的生成,n 個元素的集合,真子集共有 2^n - 1 個,每一個數字都是一個二進位制的 0/1 序列
還有一個問題是10進位制轉化為2進位制時,前面的 0 會被省略,我們需要上去
2.程式碼實現,一步步解決問題
class one
{
public static function work($arr){
$num = count($arr);// 統計一個陣列元素個數
$min = 1;
$max = bindec(str_repeat('1', $num));// 用二進位制求得 2^n - 1
for($i = $min;$i <= $max;$i++){
$str[] = str_pad(decbin($i), $num, '0', STR_PAD_LEFT);
// 數字轉化為2進位制並補充前面的隱含 0
}
// case 1 下面註釋程式碼為第一次嘗試,無法保留鍵值,無法處理含有相同值得陣列
/*
foreach($str as $v){
$choose = str_split($v, 1);
$temparr = array_combine($arr, $choose);// 陣列值作為鍵值, 便於下面選出
$out[] = array_keys($temparr, 1);
}
return $out;
*/
// case 2 如下 考慮陣列 $arr 的鍵值肯定是唯一性的,所以修改為選擇鍵值,可處理任意陣列並保留鍵
foreach($str as $v){
$choose = str_split($v, 1);// 將0/1序列轉化為陣列
// 將陣列鍵值取出與0/1陣列合並
$temparr = array_combine(array_keys($arr), $choose);
//將 值為 1 的鍵值取出
$keys = array_keys($temparr, 1);
$s = null;// 定義一個空值,用來臨時儲存一個 子集陣列
foreach($keys as $value){
$s[$value] = $arr[$value];
// 如果鍵值被上面選中,則子集陣列中也儲存這個值
}
$out[] = $s;
}
return $out;
}
}
print_r(one::work(['s'=>1, 'k'=>8, 'd'=>3])); // 測試一下
// 結果如下
Array
(
[0] => Array
(
[d] => 3
)
[1] => Array
(
[k] => 8
)
[2] => Array
(
[k] => 8
[d] => 3
)
[3] => Array
(
[s] => 1
)
[4] => Array
(
[s] => 1
[d] => 3
)
[5] => Array
(
[s] => 1
[k] => 8
)
[6] => Array
(
[s] => 1
[k] => 8
[d] => 3
)
)
// 到達了希望的效果
php 書冊中的函式靈活使用可以解決很多問題,以後需要多多練習,讓自己更加熟悉
本作品採用《CC 協議》,轉載必須註明作者和本文連結