我有一个包含1000行的零件清单。这两个字段是“部件号”和“成本”。费用从1美元到2000美元不等(全部为整数)。
我想创建一个列表,其中包含组合成本在一个较窄范围内的所有零件组合(范围的差距永远不会超过50美元)。例如,给定70-75美元的范围,返回的列表将是:
我的第一个想法是迭代可以满足标准的所有可能的部分组合(即< =范围的上限数)并报告其总和落在该范围内的那些组合。很快就会明显失败,因为组合的数量变得非常快。但鉴于大多数组合不符合标准,这个问题是否可以合理解决?
鉴于数据位于MySQL数据库的表中,我首选的解决方案是SQL或Stored Proc,下一个首选PHP,最后是Javascript。
(ETA:@piotrm发现的漏洞)
答案 0 :(得分:3)
您必须限制总费用的最大值,或者无论您如何尝试找到它们,组合的数量都将上升到天空。在以下示例中,它仅限于75,但您可以尝试其他值来查看它,您仍然可以在合理的时间内找到结果。
您还可以调整此解决方案以更新主表的插入或更新组合表,让您可以非常快速地获得不超出设定限制的任何范围的结果(但显然减慢了插入,因为它完成了所有工作)。
创建表格和触发器:
CREATE TABLE `total_max75` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`parts` varchar(255) NOT NULL,
`num` int(11) NOT NULL,
`total` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `total` (`total`,`num`)
);
CREATE TABLE `newparts` (
`name` char(4) NOT NULL,
`price` int(11) NOT NULL,
PRIMARY KEY (`name`)
);
DELIMITER //
CREATE TRIGGER addtotal AFTER INSERT ON newparts
FOR EACH ROW
BEGIN
IF NEW.price <= 75 THEN
INSERT INTO total_max75 ( parts, num, total )
SELECT CONCAT( t.parts, ', ', NEW.name),
t.num+1, t.total+NEW.price
FROM total_max75 t
WHERE t.total <= 75 - NEW.price AND num < 40;
INSERT INTO total_max75( parts, num, total )
VALUES( NEW.name, 1, NEW.price );
END IF;
END//
DELIMITER ;
然后使用:
填充INSERT INTO newparts( name, price )
SELECT part_number, cost FROM yourtable
WHERE cost <= 75;
或(作为测试数据)
INSERT INTO newparts( name, price ) VALUES
('A012', 5),('A034', 1),('A084', 10),('A094', 25),('A233', 75),
('A343', 75),('A370', 50),('B309', 13),('C124', 78);
最后使用:
获得结果SELECT * FROM total_max75 WHERE total BETWEEN 70 AND 75;
您可以在此处放置任何范围,最大值小于75(或者您在表创建部分和触发器中设置为限制的任何范围)。
结果:
A084, A370, B309 73 (you missed this one in your question)
A034, A084, A370, B309 74
A233 75
A343 75
A094, A370 75
答案 1 :(得分:2)
好吧,我的第一个想法是只自动加入表中成本低于范围高端的行:
select
l.part as `part1`, r.part as `part2`, l.cost + r.cost as `total cost`
from (select * from parts where cost < MAX_COST) l
inner join
(select * from parts where cost < MAX_COST) r
on l.cost + r.cost between MIN_COST and MAX_COST
这有多高效?
MAX_COST
的O(n ^ 2)。如果该数字不是很小,这可能会很慢parts
中行数中的O(n)。如果parts
非常大,这可能是决定因素。但如果parts
不是那么大,或者如果(1)不小,这将被(1)答案 2 :(得分:1)
我有一个递归的(在数组上):
$data = array(
'A034' => 1,
'A012' => 5,
'A084' => 10,
...
)
我们需要先使用arsort()
对数据进行排序,并使用最高值:
arsort( $data, SORT_NUMERIC);
这将确保在早期阶段处理大部分阵列。关于主要功能:
/**
* Builds resulting array
*
* @param $array $key => $price pairs
* @param $price lower price of interval (for <70,130>, it's 70)
* @param $reserve difference between lower and higher price (for <70,130>, it's 130-70 = 59)
* @param &$result output array
* @param $cummulatedKeys so far collected keys, leave empty
*
* @return void
*/
function buildResults( $array, $price, $reserve, &$result, $cumulatedKeys = array()){
// Get key of first element
reset( $array);
$key = key( $array);
// Just decrease number of elements as fast as possible
while( $one = array_shift( $array)){
$tmp = $price - $one;
// Ok reached one price
if( $tmp >= 0){
// In interval
if( (-$tmp) <= $reserve){
$result[] = array_merge( $cumulatedKeys, array( $key));
} else {
// We are too low
continue;
}
}
// Skip very small values which can't accumulate price
if( (count( $array) * $one) < $tmp){
break;
}
// We may go on, deeper
buildResults( $array, $tmp, $reserve, $result, array_merge( $cumulatedKeys, array( $key)));
// Actualize key
if( !count( $array)){
break;
}
reset( $array);
$key = key( $array);
}
}
用法应该是显而易见的,但仅针对这种情况,假设您希望处理$array
值为区间<70,90>
;
$result = array();
buildResults( $array, 70, 90-70, $result);
我没有测试过,我对它的表现感到好奇......请在评论中留言