bool KnapsackSolver(const CAmount& nTargetValue, std::vector<CInputCoin>& vCoins, std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet) { setCoinsRet.clear(); nValueRet = 0; // List of values less than target boost::optional<CInputCoin> coinLowestLarger; std::vector<CInputCoin> vValue; CAmount nTotalLower = 0; random_shuffle(vCoins.begin(), vCoins.end(), GetRandInt); for (const CInputCoin &coin : vCoins) { if (coin.txout.nValue == nTargetValue) { setCoinsRet.insert(coin); nValueRet += coin.txout.nValue; return true; } else if (coin.txout.nValue < nTargetValue + MIN_CHANGE) { vValue.push_back(coin); nTotalLower += coin.txout.nValue; } else if (!coinLowestLarger || coin.txout.nValue < coinLowestLarger->txout.nValue) { coinLowestLarger = coin; } } if (nTotalLower == nTargetValue) { for (const auto& input : vValue) { setCoinsRet.insert(input); nValueRet += input.txout.nValue; } return true; } if (nTotalLower < nTargetValue) { if (!coinLowestLarger) return false; setCoinsRet.insert(coinLowestLarger.get()); nValueRet += coinLowestLarger->txout.nValue; return true; } // Solve subset sum by stochastic approximation std::sort(vValue.begin(), vValue.end(), descending); std::vector<char> vfBest; CAmount nBest; ApproximateBestSubset(vValue, nTotalLower, nTargetValue, vfBest, nBest); if (nBest != nTargetValue && nTotalLower >= nTargetValue + MIN_CHANGE) ApproximateBestSubset(vValue, nTotalLower, nTargetValue + MIN_CHANGE, vfBest, nBest); // If we have a bigger coin and (either the stochastic approximation didn't find a good solution, // or the next bigger coin is closer), return the bigger coin if (coinLowestLarger && ((nBest != nTargetValue && nBest < nTargetValue + MIN_CHANGE) || coinLowestLarger->txout.nValue <= nBest)) { setCoinsRet.insert(coinLowestLarger.get()); nValueRet += coinLowestLarger->txout.nValue; } else { for (unsigned int i = 0; i < vValue.size(); i++) if (vfBest[i]) { setCoinsRet.insert(vValue[i]); nValueRet += vValue[i].txout.nValue; } if (LogAcceptCategory(BCLog::SELECTCOINS)) { LogPrint(BCLog::SELECTCOINS, "SelectCoins() best subset: "); /* Continued */ for (unsigned int i = 0; i < vValue.size(); i++) { if (vfBest[i]) { LogPrint(BCLog::SELECTCOINS, "%s ", FormatMoney(vValue[i].txout.nValue)); /* Continued */ } } LogPrint(BCLog::SELECTCOINS, "total %s\n", FormatMoney(nBest)); } } return true; }
bool KnapsackSolver(const CAmount& nTargetValue, std::vector<OutputGroup>& groups, std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet) { setCoinsRet.clear(); nValueRet = 0; // List of values less than target boost::optional<OutputGroup> lowest_larger; std::vector<OutputGroup> applicable_groups; CAmount nTotalLower = 0; random_shuffle(groups.begin(), groups.end(), GetRandInt); for (const OutputGroup& group : groups) { if (group.m_value == nTargetValue) { util::insert(setCoinsRet, group.m_outputs); nValueRet += group.m_value; return true; } else if (group.m_value < nTargetValue + MIN_CHANGE) { applicable_groups.push_back(group); nTotalLower += group.m_value; } else if (!lowest_larger || group.m_value < lowest_larger->m_value) { lowest_larger = group; } } if (nTotalLower == nTargetValue) { for (const auto& group : applicable_groups) { util::insert(setCoinsRet, group.m_outputs); nValueRet += group.m_value; } return true; } if (nTotalLower < nTargetValue) { if (!lowest_larger) return false; util::insert(setCoinsRet, lowest_larger->m_outputs); nValueRet += lowest_larger->m_value; return true; } // Solve subset sum by stochastic approximation std::sort(applicable_groups.begin(), applicable_groups.end(), descending); std::vector<char> vfBest; CAmount nBest; ApproximateBestSubset(applicable_groups, nTotalLower, nTargetValue, vfBest, nBest); if (nBest != nTargetValue && nTotalLower >= nTargetValue + MIN_CHANGE) { ApproximateBestSubset(applicable_groups, nTotalLower, nTargetValue + MIN_CHANGE, vfBest, nBest); } // If we have a bigger coin and (either the stochastic approximation didn't find a good solution, // or the next bigger coin is closer), return the bigger coin if (lowest_larger && ((nBest != nTargetValue && nBest < nTargetValue + MIN_CHANGE) || lowest_larger->m_value <= nBest)) { util::insert(setCoinsRet, lowest_larger->m_outputs); nValueRet += lowest_larger->m_value; } else { for (unsigned int i = 0; i < applicable_groups.size(); i++) { if (vfBest[i]) { util::insert(setCoinsRet, applicable_groups[i].m_outputs); nValueRet += applicable_groups[i].m_value; } } if (LogAcceptCategory(BCLog::SELECTCOINS)) { LogPrint(BCLog::SELECTCOINS, "SelectCoins() best subset: "); /* Continued */ for (unsigned int i = 0; i < applicable_groups.size(); i++) { if (vfBest[i]) { LogPrint(BCLog::SELECTCOINS, "%s ", FormatMoney(applicable_groups[i].m_value)); /* Continued */ } } LogPrint(BCLog::SELECTCOINS, "total %s\n", FormatMoney(nBest)); } } return true; }