本旨
問題を解くとき、各ステップは最終的にグローバルな最適解を得ることを期待して、ローカルな最適解を選択します。
(欲張りアルゴリズムの最終結果は、必ずしもグローバルな最適解ではありませんが、実際にはおおよその最適解です。)
古典的な問題1-集合被覆問題
n個のセットがあり、各セットにはいくつかの要素が含まれています。それらからm個のセットを見つけるには、n個のセットにすべての要素が含まれている必要があり、mが最小です。
一般的な解決策:
(1)nセットのすべての組み合わせをリストします。これは、各セットがセットに含まれる場合と含まれない場合があるため、合計2 n 2 ^ nになるためです。2n種類の組み合わせスキーム。
(2)これらの組み合わせスキームで、すべての要素を含むセットの組み合わせを見つけ、その組み合わせに含まれるセットの数が最も少なくなります。
この方法の時間計算量はO(2 n)O(2 ^ n)です。O (2n)、nが増加すると、時間は急激に増加し、使用できなくなります。
欲張りアルゴリズムのアイデア(近似アルゴリズム):(
1)最も含まれていない要素を含むセットを選択します。
(2)選択したセットにすべての要素が含まれるまで、最初の手順を繰り返します。
欲張りアルゴリズムの時間計算量はO(n 2)O(n ^ 2)です。O (n2)。
例:
次のようにいくつかのセットがあり
ます
。set1= {1、2、3};
set2 = {1、3、4};set3 = {2、5、6};set4 = {2、3}を
解きます。;
set5 = {6、7};
コード:
public class GreedyAlgorithm {
public Set<Integer> greedy(List<Set<Integer>> setList) {
//收集所有集合中的所有元素
Set<Integer> needed = new HashSet<>();
for (Set<Integer> set : setList) {
for (int i : set) {
needed.add(i);
}
}
Set<Integer> result = new HashSet<>();
while (!needed.isEmpty()) {
int bestSet = 0;
int bestCoveredSize = 0;
for (int i = 0; i < setList.size(); i++) {
if (result.contains(i)) {
continue;
}
int coveredSize = 0;
for (int j : setList.get(i)) {
if (needed.contains(j)) {
coveredSize++;
}
}
//体现出贪心算法,每次都选含有最多未包含元素的集合
if (coveredSize > bestCoveredSize) {
bestSet = i;
bestCoveredSize = coveredSize;
}
}
result.add(bestSet);
needed.removeAll(setList.get(bestSet));
}
return result;
}
public static void main(String[] args) {
List<Set<Integer>> setList = new ArrayList<>();
setList.add(new HashSet<>(Arrays.asList(1, 2, 3)));
setList.add(new HashSet<>(Arrays.asList(1, 3, 4)));
setList.add(new HashSet<>(Arrays.asList(2, 5, 6)));
setList.add(new HashSet<>(Arrays.asList(2, 3)));
setList.add(new HashSet<>(Arrays.asList(6, 7)));
System.out.println(new GreedyAlgorithm().greedy(setList));
}
}
古典的な問題2-巡回セールスマン問題
A市から他のn都市への移動が必要な旅行代理店があります。総移動距離が最短になるように旅行ルートを計画してください。
一般的な解決策:n!N!を
計算します。n !移動ルートを選択し、その中から最短ルートを選択します。時間計算量はO(n!)O(n!)です。O (n !)。
欲張りアルゴリズムのアイデア:
行きたい次の都市を選択するたびに、行ったことのない最も近い都市を選択します。
コード
public class GreedyAlgorithm2 {
public static int[] greedyAlgorithm2(int[][] distance, int n) {
int[] result = new int[n];
Set<Integer> notBeenTo = new HashSet<>(n);
for (int i = 1; i <= n; i++) {
notBeenTo.add(i);
}
int from = 0;
for (int i = 0; i < result.length; i++) {
int next = 0;
for (int j : notBeenTo) {
//体现出贪心算法,每次都选还没去过的距离最近的城市
if (next == 0 || distance[from][next] > distance[from][j]) {
next = j;
}
}
result[i] = next;
from = next;
notBeenTo.remove(next);
}
return result;
}
//测试代码
public static void main(String[] args) {
int n = 4;
int[][] distance = new int[n + 1][n + 1];
//0表示旅行商当前所在城市A, 1...n表示要去的城市
Random rnd = new Random();
for (int i = 0; i < distance.length; i++) {
for (int j = 0; j < i; j++) {
if (i != j) {
distance[i][j] = distance[j][i] = rnd.nextInt(10) + 1;
}
}
}
for (int[] arr : distance) {
System.out.println(Arrays.toString(arr));
}
System.out.println();
int[] result = greedyAlgorithm2(distance, n);
System.out.println(Arrays.toString(result));
}
}
NP完全問題
P問題、NP問題、NP完全問題(NPC)、NP困難問題(NPH)とは、Baiduだけでお願いしますが、満足のいく答えが見つかりませんでした(理解できませんでした)。
- NP完全問題の簡単な定義は、集合被覆問題や巡回セールスマン問題など、解決が難しい問題(多項式時間では解決できない問題)として知られています。
- 問題がNP完全であると判断する場合、完全な解決策を見つける必要はありませんが、近似アルゴリズムを使用する必要があります。しかし、問題がNP完全であるかどうかを判断することは困難です。これは、解決が容易な問題とNP完全問題の差が通常非常に小さいためです。「2点間の最短経路問題」や「巡回セールスマン問題」など。
次の方法は、問題がNP完全であるかどうかを単純に(必ずしも)判断するために使用できます。
- 要素の数が少ない場合、アルゴリズムは非常に高速に実行されますが、要素の数が増えると、速度は非常に遅くなります。
- 「すべての組み合わせ」に関連する問題は、通常、NP完全です。
- 問題を小さな問題に分割することはできません。考えられるすべての状況を考慮する必要があります。これはNP完全問題である可能性があります。
- 問題がシーケンス(巡回セールスマン問題の都市シーケンスなど)に関係していて、解決が難しい場合は、NP完全問題である可能性があります。
- 問題がコレクション(放送局のコレクションなど)に関係していて、解決が難しい場合は、NP完全問題である可能性があります。
- 問題が集合被覆問題または旅行者問題に変換できる場合、それはNP完全でなければなりません。
参照:「アルゴリズム図」第8章欲張りアルゴリズム