我以正常方式完成了广度优先搜索。 现在我试图以多线程的方式做到这一点。 我有一个在线程之间共享的队列。 我从队列中删除节点时使用synchronize(LockObject)(FIFI队列) 所以我想要做的就是这样。 当我的线程找到解决方案时,所有其他线程将立即停止。
答案 0 :(得分:2)
我假设您正在为您的BFS遍历一棵树。
创建一个线程池。 对于节点中的每个未探测的子节点,从线程池中检索线程(可能使用Semaphore)。将子节点标记为“已探索”并以BFS方式探索节点的子节点。当您找到解决方案或完成所有节点的探索时,请释放信号量。
^我之前从未这样做过,所以我可能错过了一些东西。
答案 1 :(得分:2)
假设您想要迭代地执行此操作(请参阅底部的注释,为什么可能有更好的封闭解决方案),这对于执行多线程来说不是一个很大的问题。问题是如果你不依赖于以前的结果,多线程是很好的,但在这里你需要最少量的硬币。
正如您所指出的,广度优先解决方案可确保一旦达到所需金额,您就无法在单线程环境中获得更少硬币的进一步解决方案。但是,在多线程环境中,一旦开始计算解决方案,就不能保证它会在其他解决方案之前完成。让我们想象值21:它可以是20c硬币和1c或4c 5c硬币和1c;如果两者同时计算,则无法保证第一个(和正确的)解决方案将首先完成。在实践中,情况不太可能发生,但是当您使用多线程时,您希望解决方案在理论上工作,因为多线程总是在演示中失败,无论它们是否应该在宇宙死亡之前都不会失败。
现在你有两种可能的解决方案:一种是在每个级别的开头引入阻塞点;你不会在上一级完成之前就开始这个级别。另一种是一旦你达到一个解决方案,继续以比当前结果更低的水平进行所有计算(这意味着你不能清除其他的)。可能需要所有同步,你可以通过单线程获得更好的性能,但让我们继续。
对于第一种解决方案,自然形式是迭代增加水平。您可以使用happymeal提供的解决方案和Semaphore。另一种方法是使用java提供的新类。
CoinSet getCoinSet(int desiredAmount) throws InterruptedException {
// Use whatever number of threads you prefer or another member of Executors.
final ExecutorService executor = Executors.newFixedThreadPool(10);
ResultContainer container = new ResultContainer();
container.getNext().add(new Producer(desiredAmount, new CoinSet(), container));
while (container.getResult() == null) {
executor.invokeAll(container.setNext(new Vector<Producer>()));
}
return container.getResult();
}
public class Producer implements Callable<CoinSet> {
private final int desiredAmount;
private final CoinSet data;
private final ResultContainer container;
public Producer(int desiredAmount, CoinSet data, ResultContainer container) {
this.desiredAmount = desiredAmount;
this.data = data;
this.container = container;
}
public CoinSet call() {
if (data.getSum() == desiredAmount) {
container.setResult(data);
return data;
} else {
Collection<CoinSet> nextSets = data.addCoins();
for (CoinSet nextSet : nextSets) {
container.getNext().add(new Producer(desiredAmount, nextSet, container));
}
return null;
}
}
}
// Probably it is better to split this class, but you then need to pass too many parameters
// The only really needed part is to create a wrapper around getNext, since invokeAll is
// undefined if you modify the list of tasks.
public class ResultContainer {
// I use Vector because it is synchronized.
private Vector<Producer> next = new Vector<Producer>();
private CoinSet result = null;
// Note I return the existing value.
public Vector<Producer> setNext(Vector<Producer> newValue) {
Vector<Producer> current = next;
next = newValue;
return current;
}
public Vector<Producer> getNext() {
return next;
}
public synchronized void setResult(CoinSet newValue) {
result = newValue;
}
public synchronized CoinSet getResult() {
return result;
}
}
这仍然存在执行现有任务的问题;但是,修复它很简单;将线程执行器传递给每个Producer(或容器)。然后,当您找到结果时,请调用executor.shutdownNow。正在执行的线程不会被中断,但每个线程中的操作都是微不足道的,所以它会快速完成;没有开始的可运行的东西不会开始。
第二个选项意味着您必须完成所有当前任务,除非您跟踪每个级别运行的任务数。但是,您不再需要跟踪级别,并且您不需要while循环。相反,你只需要打电话
executor.submit(new Producer(new CoinSet(), desiredAmount, container)).get();
然后,调用方法非常相似(假设您在Producer中有执行程序):
public CoinSet call() {
if (container.getResult() != null && data.getCount() < container.getResult().getCount()) {
if (data.getSum() == desiredAmount)) {
container.setResult(data);
return data;
} else {
Collection<CoinSet> nextSets = data.addCoins();
for (CoinSet nextSet : nextSets) {
executor.submit(new Producer(desiredAmount, nextSet, container));
}
return null;
}
}
}
并且你还必须修改container.setResult,因为你不能在if和设置值之间依赖它而没有被其他一些线程设置(线程真的很烦人,不是吗?)
public synchronized void setResult(CoinSet newValue) {
if (newValue.getCount() < result.getCount()) {
result = newValue;
}
}
在之前的所有答案中,CoinSet.getSum()返回集合中硬币的总和,CoinSet.getCount()返回硬币数量,CoinSet.addCoins()返回CoinSet集合,其中每个元素都是当前的CoinSet加上每个可能不同值的一个硬币
注意:对于值为1,5,10和20的硬币问题,最简单的解决方案是取金额并除以最大的硬币。然后取模数并使用下一个最大值,依此类推。这是你需要的最低金额。当以下属性为真时,此规则适用(AFAICT):如果对于所有连续的硬币值对(即在这种情况下,1-5,5-10,10-20),您可以达到下部元素的任何int倍数使用较大元素的硬币数量较少的对和必要的硬币。你只需要证明它对该对中两个元素的最小公倍数(之后它重复自己)
答案 2 :(得分:2)
我从你对happymeal's anwer的评论中收集到你正在试图通过添加1c,5c,10c和20c的硬币来找到如何达到特定数量的金额。
由于每个硬币面额除以下一个较大硬币的面额,这可以在如下的恒定时间内解决:
int[] coinCount(int amount) {
int[] coinValue = {20, 10, 5, 1};
int[] coinCount = new int[coinValue.length];
for (int i = 0; i < coinValue.length; i++) {
coinCount[i] = amount / coinValue[i];
amount -= coinCount[i] * coinValue[i];
}
return coinCount;
}
带回家消息:尝试在求助多线程之前优化算法,因为算法改进可以带来更大的改进。
答案 3 :(得分:2)
我已经成功实施了它。 我做的是我在第一级别中占用所有节点,假设有4个节点。 然后我有2个线程。每个人需要2个节点并生成他们的孩子。每当博德找到解决方案时,他必须报告他找到解决方案的级别并限制搜索级别,以便其他线程不要超过级别。 只应报告同步报告方法。
我做了硬币改变问题的代码: 这是我的代码供其他人使用 - 不完美,但做的工作:) -
主类(CoinsProblemBFS.java)
package coinsproblembfs;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Scanner;
/**
*
* @author Kassem M. Bagher
*/
public class CoinsProblemBFS
{
private static List<Item> MoneyList = new ArrayList<Item>();
private static Queue<Item> q = new LinkedList<Item>();
private static LinkedList<Item> tmpQ;
public static Object lockLevelLimitation = new Object();
public static int searchLevelLimit = 1000;
public static Item lastFoundNode = null;
private static int numberOfThreads = 2;
private static void InitializeQueu(Item Root)
{
for (int x = 0; x < MoneyList.size(); x++)
{
Item t = new Item();
t.value = MoneyList.get(x).value;
t.Totalvalue = MoneyList.get(x).Totalvalue;
t.Title = MoneyList.get(x).Title;
t.parent = Root;
t.level = 1;
q.add(t);
}
}
private static int[] calculateQueueLimit(int numberOfItems, int numberOfThreads)
{
int total = 0;
int[] queueLimit = new int[numberOfThreads];
for (int x = 0; x < numberOfItems; x++)
{
if (total < numberOfItems)
{
queueLimit[x % numberOfThreads] += 1;
total++;
}
else
{
break;
}
}
return queueLimit;
}
private static void initializeMoneyList(int numberOfItems, Item Root)
{
for (int x = 0; x < numberOfItems; x++)
{
Scanner input = new Scanner(System.in);
Item t = new Item();
System.out.print("Enter the Title and Value for item " + (x + 1) + ": ");
String tmp = input.nextLine();
t.Title = tmp.split(" ")[0];
t.value = Double.parseDouble(tmp.split(" ")[1]);
t.Totalvalue = t.value;
t.parent = Root;
MoneyList.add(t);
}
}
private static void printPath(Item item)
{
System.out.println("\nSolution Found in Thread:" + item.winnerThreadName + "\nExecution Time: " + item.searchTime + " ms, " + (item.searchTime / 1000) + " s");
while (item != null)
{
for (Item listItem : MoneyList)
{
if (listItem.Title.equals(item.Title))
{
listItem.counter++;
}
}
item = item.parent;
}
for (Item listItem : MoneyList)
{
System.out.println(listItem.Title + " x " + listItem.counter);
}
}
public static void main(String[] args) throws InterruptedException
{
Item Root = new Item();
Root.Title = "Root Node";
Scanner input = new Scanner(System.in);
System.out.print("Number of Items: ");
int numberOfItems = input.nextInt();
input.nextLine();
initializeMoneyList(numberOfItems, Root);
System.out.print("Enter the Amount of Money: ");
double searchValue = input.nextDouble();
int searchLimit = (int) Math.ceil((searchValue / MoneyList.get(MoneyList.size() - 1).value));
System.out.print("Number of Threads (Muste be less than the number of items): ");
numberOfThreads = input.nextInt();
if (numberOfThreads > numberOfItems)
{
System.exit(1);
}
InitializeQueu(Root);
int[] queueLimit = calculateQueueLimit(numberOfItems, numberOfThreads);
List<Thread> threadList = new ArrayList<Thread>();
for (int x = 0; x < numberOfThreads; x++)
{
tmpQ = new LinkedList<Item>();
for (int y = 0; y < queueLimit[x]; y++)
{
tmpQ.add(q.remove());
}
BFS tmpThreadObject = new BFS(MoneyList, searchValue, tmpQ);
Thread t = new Thread(tmpThreadObject);
t.setName((x + 1) + "");
threadList.add(t);
}
for (Thread t : threadList)
{
t.start();
}
boolean finish = false;
while (!finish)
{
Thread.sleep(250);
for (Thread t : threadList)
{
if (t.isAlive())
{
finish = false;
break;
}
else
{
finish = true;
}
}
}
printPath(lastFoundNode);
}
}
项目类(Item.java)
package coinsproblembfs;
/**
*
* @author Kassem
*/
public class Item
{
String Title = "";
double value = 0;
int level = 0;
double Totalvalue = 0;
int counter = 0;
Item parent = null;
long searchTime = 0;
String winnerThreadName="";
}
主题类(BFS.java)
package coinsproblembfs;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
/**
*
* @author Kassem M. Bagher
*/
public class BFS implements Runnable
{
private LinkedList<Item> q;
private List<Item> MoneyList;
private double searchValue = 0;
private long start = 0, end = 0;
public BFS(List<Item> monyList, double searchValue, LinkedList<Item> queue)
{
q = new LinkedList<Item>();
MoneyList = new ArrayList<Item>();
this.searchValue = searchValue;
for (int x = 0; x < queue.size(); x++)
{
q.addLast(queue.get(x));
}
for (int x = 0; x < monyList.size(); x++)
{
MoneyList.add(monyList.get(x));
}
}
private synchronized void printPath(Item item)
{
while (item != null)
{
for (Item listItem : MoneyList)
{
if (listItem.Title.equals(item.Title))
{
listItem.counter++;
}
}
item = item.parent;
}
for (Item listItem : MoneyList)
{
System.out.println(listItem.Title + " x " + listItem.counter);
}
}
private void addChildren(Item node, LinkedList<Item> q, boolean initialized)
{
for (int x = 0; x < MoneyList.size(); x++)
{
Item t = new Item();
t.value = MoneyList.get(x).value;
if (initialized)
{
t.Totalvalue = 0;
t.level = 0;
}
else
{
t.parent = node;
t.Totalvalue = MoneyList.get(x).Totalvalue;
if (t.parent == null)
{
t.level = 0;
}
else
{
t.level = t.parent.level + 1;
}
}
t.Title = MoneyList.get(x).Title;
q.addLast(t);
}
}
@Override
public void run()
{
start = System.currentTimeMillis();
try
{
while (!q.isEmpty())
{
Item node = null;
node = (Item) q.removeFirst();
node.Totalvalue = node.value + node.parent.Totalvalue;
if (node.level < CoinsProblemBFS.searchLevelLimit)
{
if (node.Totalvalue == searchValue)
{
synchronized (CoinsProblemBFS.lockLevelLimitation)
{
CoinsProblemBFS.searchLevelLimit = node.level;
CoinsProblemBFS.lastFoundNode = node;
end = System.currentTimeMillis();
CoinsProblemBFS.lastFoundNode.searchTime = (end - start);
CoinsProblemBFS.lastFoundNode.winnerThreadName=Thread.currentThread().getName();
}
}
else
{
if (node.level + 1 < CoinsProblemBFS.searchLevelLimit)
{
addChildren(node, q, false);
}
}
}
}
} catch (Exception e)
{
e.printStackTrace();
}
}
}
示例输入:
Number of Items: 4
Enter the Title and Value for item 1: one 1
Enter the Title and Value for item 2: five 5
Enter the Title and Value for item 3: ten 10
Enter the Title and Value for item 4: twenty 20
Enter the Amount of Money: 150
Number of Threads (Muste be less than the number of items): 2