From 9b8ba51387d0a8db467bafaf7ecd09d93bded873 Mon Sep 17 00:00:00 2001 From: skyouc Date: 星期一, 28 四月 2025 13:23:37 +0800 Subject: [PATCH] 添加简略出库策略 --- /dev/null | 90 ---------- rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/WaveItem.java | 10 + rsf-server/src/main/Test/CombinationFinder.java | 123 +++++++++++++ rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/WaveServiceImpl.java | 60 +++-- rsf-admin/src/page/orders/wave/ItemToTaskModal.jsx | 48 +++- rsf-admin/src/i18n/zh.js | 4 rsf-admin/src/i18n/en.js | 5 rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/WaveController.java | 6 rsf-server/src/main/java/com/vincent/rsf/server/manager/utils/OptimalAlgorithmUtil.java | 174 ++++++++++++++---- 9 files changed, 342 insertions(+), 178 deletions(-) diff --git a/rsf-admin/src/i18n/en.js b/rsf-admin/src/i18n/en.js index 3a583e9..6207c83 100644 --- a/rsf-admin/src/i18n/en.js +++ b/rsf-admin/src/i18n/en.js @@ -70,6 +70,7 @@ loadMore: 'Load More Data', complete: 'Complete', deprecate: 'Deprecate', + stockError: 'Insuff stock ', inputPlaceholder: 'Use commas to separate', resend: 'RESEND', selected: 'selected', @@ -746,7 +747,9 @@ anfme: "anfme", workQty: "workQty", qty: "Qty", - stockLocs: "Stock Locs" + stockLocs: "Stock Locs", + stockQty: "Stock Qty", + }, task: { taskCode: "TaskCode", diff --git a/rsf-admin/src/i18n/zh.js b/rsf-admin/src/i18n/zh.js index af8f418..53d905e 100644 --- a/rsf-admin/src/i18n/zh.js +++ b/rsf-admin/src/i18n/zh.js @@ -71,6 +71,7 @@ loadMore: '鍔犺浇鏇村', complete: '瀹屾垚', deprecate: '搴熷純', + stockError: '搴撳瓨涓嶈冻', resend: '閲嶅彂', selected: '椤归�変腑', batch: '鎵归噺缂栬緫' @@ -792,7 +793,8 @@ anfme: "鏁伴噺", workQty: "鎵ц鏁�", qty: "瀹屾垚鏁伴噺", - stockLocs: "搴撳瓨浣嶇疆" + stockLocs: "搴撳瓨浣嶇疆", + stockQty: "搴撳瓨鏁伴噺", }, task: { taskCode: "浠诲姟鍙�", diff --git a/rsf-admin/src/page/orders/wave/ItemToTaskModal.jsx b/rsf-admin/src/page/orders/wave/ItemToTaskModal.jsx index 6b6c39e..a0b4d7e 100644 --- a/rsf-admin/src/page/orders/wave/ItemToTaskModal.jsx +++ b/rsf-admin/src/page/orders/wave/ItemToTaskModal.jsx @@ -37,11 +37,10 @@ ListContextProvider, useList, Toolbar, - SingleFieldList, } from 'react-admin'; import { PAGE_DRAWER_WIDTH, OPERATE_MODE, DEFAULT_PAGE_SIZE } from '@/config/setting'; -import { Box, Typography, Card, Stack, DialogContent, DialogTitle, DialogActions, Dialog } from '@mui/material'; +import { Box, Typography, Card, Stack, DialogContent, DialogTitle, DialogActions, Dialog, Chip, ListItem, Paper } from '@mui/material'; import { styled } from '@mui/material/styles'; import DialogCloseButton from "../../components/DialogCloseButton"; import request from '@/utils/request'; @@ -131,7 +130,6 @@ <NumberField source="waveId" label="table.field.waveItem.waveId" /> <TextField source="waveCode" label="table.field.waveItem.waveCode" /> <TextField source="orderCode" label="table.field.waveItem.orderCode" /> - {/* <TextField source="maktx" label="table.field.waveItem.matnrName" /> */} <NumberField source="matnrId" label="table.field.waveItem.matnrId" /> <TextField source="matnrCode" label="table.field.waveItem.matnrCode" /> <TextField source="batch" label="table.field.waveItem.batch" /> @@ -143,22 +141,16 @@ <NumberField source="anfme" label="table.field.waveItem.anfme" /> <NumberField source="workQty" label="table.field.waveItem.workQty" /> <NumberField source="qty" label="table.field.waveItem.qty" /> + <NumberField source="stockQty" label="table.field.waveItem.stockQty" /> <WrapperField cellClassName="opt" label="table.field.waveItem.stockLocs"> - <ArrayField source="stockLocs" stockLocs="table.field.waveItem.stockLocs"> - <SingleFieldList linkType={false}> - <ChilpField source="" size="small"></ChilpField> - </SingleFieldList> - </ArrayField> - {/* <NumberField source="anfme" label="table.field.waveItem.stockLocs" /> */} - {/* <EditButton sx={{ padding: '1px', fontSize: '.75rem' }} /> - <DeleteButton sx={{ padding: '1px', fontSize: '.75rem' }} mutationMode={OPERATE_MODE} /> */} + <TagsField /> </WrapperField> </StyledDatagrid> </ListContextProvider> </DialogContent> <DialogActions> <Toolbar sx={{ width: '100%', justifyContent: 'end' }} > - <GenerateTaskButton record={[record?.id]} /> + <GenerateTaskButton record={[record?.id]} dataSource={data} /> </Toolbar> </DialogActions> </Dialog> @@ -168,12 +160,12 @@ export default ItemToTaskModal; -const GenerateTaskButton = (record) => { +const GenerateTaskButton = (record, dataSource) => { const refresh = useRefresh(); const notify = useNotify(); const redirect = useRedirect(); const generateTask = async () => { - const res = await request.post(`/wave/public/task`, { ids: record?.record }); + const res = await request.post(`/wave/public/task`, { wave: dataSource }); if (res?.data?.code === 200) { notify(res.data.msg); redirect("/task") @@ -183,4 +175,32 @@ refresh(); } return (<Button variant="contained" label={"ra.action.save"} onClick={generateTask}></Button>) +} + +const TagsField = () => { + const record = useRecordContext(); + const translate = useTranslate(); + const locs = JSON.parse(record.stockLocs); + if (locs == undefined || locs.length < 1) { + return ( + <> + <ListItem> + <Chip color="error" label={translate("common.action.stockError")} variant="outlined" /> + </ListItem> + </> + ) + } else { + return ( + <> + {locs.map((data) => { + return ( + <ListItem key={data?.id}> + <Chip label={data?.locCode} /> + </ListItem> + ) + })} + </> + ) + } + } \ No newline at end of file diff --git a/rsf-server/src/main/Test/CombinationFinder.java b/rsf-server/src/main/Test/CombinationFinder.java new file mode 100644 index 0000000..cd7ee08 --- /dev/null +++ b/rsf-server/src/main/Test/CombinationFinder.java @@ -0,0 +1,123 @@ +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class CombinationFinder { + + public List<Integer> findCombination(double[] nums, double target) { + // 澶勭悊绛夊�兼儏鍐� + List<Integer> equal = findEqual(nums, target); + if (equal != null && !equal.isEmpty()) { + return equal; + } + + // 澶勭悊澶т簬鐨勬儏鍐� + List<Integer> greater = findGreater(nums, target); + if (greater != null && !greater.isEmpty()) { + return greater; + } + + // 澶勭悊灏忎簬鐨勬儏鍐� + List<Integer> less = findLess(nums, target); + return less != null ? less : new ArrayList<>(); // 纭繚涓嶈繑鍥瀗ull + } + + private List<Integer> findEqual(double[] nums, double target) { + int n = nums.length; + double[] dp = new double[(int) (target * 100 + 1)]; // 鏀惧ぇ100鍊嶅鐞嗙簿搴﹂棶棰� + Arrays.fill(dp, Double.MAX_VALUE); + dp[0] = 0; + int[] prev = new int[(int) (target * 100 + 1)]; + Arrays.fill(prev, -1); + + for (int j = 0; j < n; j++) { + int num = (int) (nums[j] * 100); // 鏀惧ぇ100鍊嶅鐞嗙簿搴﹂棶棰� + for (int i = (int) (target * 100); i >= num; i--) { + if (dp[i - num] != Double.MAX_VALUE && dp[i - num] + 1 < dp[i]) { + dp[i] = dp[i - num] + 1; + prev[i] = j; + } + } + } + + if (dp[(int) (target * 100)] == Double.MAX_VALUE) { + return null; + } + + List<Integer> indices = new ArrayList<>(); + int current = (int) (target * 100); + while (current > 0) { + int j = prev[current]; + if (j == -1) return null; + indices.add(j); + current -= (int) (nums[j] * 100); + } + + return indices; + } + + private List<Integer> findGreater(double[] nums, double target) { + List<double[]> sorted = new ArrayList<>(); + for (int i = 0; i < nums.length; i++) { + sorted.add(new double[]{nums[i], i}); + } + sorted.sort((a, b) -> Double.compare(b[0], a[0])); + + double sum = 0; + List<Integer> indices = new ArrayList<>(); + for (double[] pair : sorted) { + sum += pair[0]; + indices.add((int) pair[1]); + if (sum > target) { + return indices; + } + } + + return null; + } + + private List<Integer> findLess(double[] nums, double target) { + int n = nums.length; + List<Integer> bestIndices = new ArrayList<>(); + double[] maxSum = {-1.0}; + for (int k = 1; k <= n; k++) { + List<Integer> currentIndices = new ArrayList<>(); + double[] currentMax = {-1.0}; + backtrack(nums, target, 0, k, 0.0, 0, currentIndices, currentMax, new ArrayList<>()); + if (currentMax[0] != -1.0) { + if (currentMax[0] > maxSum[0] || (currentMax[0] == maxSum[0] && currentIndices.size() < bestIndices.size())) { + maxSum[0] = currentMax[0]; + bestIndices = new ArrayList<>(currentIndices); + } + } + } + + return bestIndices.isEmpty() ? null : bestIndices; + } + + private void backtrack(double[] nums, double target, int start, int k, double currentSum, int count, List<Integer> currentIndices, double[] currentMax, List<Integer> path) { + if (count == k) { + if (currentSum <= target && currentSum > currentMax[0]) { + currentMax[0] = currentSum; + currentIndices.clear(); + currentIndices.addAll(path); + } + return; + } + + for (int i = start; i < nums.length; i++) { + if (currentSum + nums[i] > target) continue; + path.add(i); + backtrack(nums, target, i + 1, k, currentSum + nums[i], count + 1, currentIndices, currentMax, path); + path.remove(path.size() - 1); + } + } + + public static void main(String[] args) { + CombinationFinder finder = new CombinationFinder(); + double[] nums = {3.0, 1.0, 4.0, 2.0}; + double target = 0.1; + List<Integer> result = finder.findCombination(nums, target); + System.out.println("鏈�浼樼粍鍚堢殑绱㈠紩: " + result); // 渚嬪锛岀瓑鍊肩粍鍚堝彲鑳戒负绱㈠紩2锛�4.0锛夊拰3锛�1.0锛� + } +} \ No newline at end of file diff --git a/rsf-server/src/main/Test/MinimalCombinationSum.java b/rsf-server/src/main/Test/MinimalCombinationSum.java deleted file mode 100644 index 624a4f1..0000000 --- a/rsf-server/src/main/Test/MinimalCombinationSum.java +++ /dev/null @@ -1,90 +0,0 @@ -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -public class MinimalCombinationSum { - - private static List<Double> bestSolution; - private static double target; - private static double minDifference; - private static final double EPSILON = 1e-10; - - public static List<Double> findBestCombination(Double[] candidates, double targetSum) { - bestSolution = null; - target = targetSum; - minDifference = Double.MAX_VALUE; - Arrays.sort(candidates); - backtrack(candidates, 0, new ArrayList<>(), 0.0); - - // 濡傛灉娌℃湁绮剧‘瑙o紝杩斿洖鏈�鎺ヨ繎鐨勮繎浼艰В - return bestSolution != null ? bestSolution : new ArrayList<>(); - } - - private static void backtrack(Double[] candidates, int start, - List<Double> current, double currentSum) { - // 璁$畻褰撳墠鍜屼笌鐩爣鐨勫樊鍊� - double difference = Math.abs(currentSum - target); - - // 濡傛灉鎵惧埌鏇翠紭瑙o紙宸�兼洿灏忔垨宸�肩浉鍚屼絾缁勫悎鏇寸煭锛� - if (difference < minDifference - EPSILON || - (Math.abs(difference - minDifference) < EPSILON && - (bestSolution == null || current.size() < bestSolution.size()))) { - minDifference = difference; - bestSolution = new ArrayList<>(current); - } - - // 濡傛灉宸茬粡鎵惧埌绮剧‘瑙o紝涓嶉渶瑕佺户缁悳绱㈡洿闀跨殑缁勫悎 - if (minDifference < EPSILON) { - return; - } - - // 閬嶅巻鍊欓�夋暟瀛� - for (int i = start; i < candidates.length; i++) { - double num = candidates[i]; - - // 鍓灊锛氬鏋滃綋鍓嶅拰宸茬粡杩滃ぇ浜庣洰鏍囧�硷紝璺宠繃 - if (currentSum + num > target + minDifference + EPSILON) { - continue; - } - - // 璺宠繃閲嶅鏁板瓧 - if (i > start && Math.abs(candidates[i] - candidates[i - 1]) < EPSILON) { - continue; - } - - current.add(num); - backtrack(candidates, i + 1, current, currentSum + num); - current.remove(current.size() - 1); - } - } - - public static void main(String[] args) { - Double[] candidates1 = {1.5, 2.3, 3.1, 4.7}; - double target1 = 3.8; - System.out.println("鍊欓�夋暟瀛�: " + Arrays.toString(candidates1)); - System.out.println("鐩爣鍊�: " + target1); - List<Double> result1 = findBestCombination(candidates1, target1); - printResult(result1, target1); - -// Double[] candidates2 = {0.8, 1.2, 1.7, 2.5}; -// double target2 = 3.9; -// System.out.println("\n鍊欓�夋暟瀛�: " + Arrays.toString(candidates2)); -// System.out.println("鐩爣鍊�: " + target2); -// List<Double> result2 = findBestCombination(candidates2, target2); -// printResult(result2, target2); - } - - private static void printResult(List<Double> result, double target) { - if (result.isEmpty()) { - System.out.println("鏃犺В"); - } else { - double sum = result.stream().mapToDouble(Double::doubleValue).sum(); - if (Math.abs(sum - target) < EPSILON) { - System.out.printf("绮剧‘瑙�: %s (鍜�=%.2f)\n", result, sum); - } else { - System.out.printf("杩戜技瑙�: %s (鍜�=%.2f, 涓庣洰鏍囩浉宸�: %.2f)\n", - result, sum, sum - target); - } - } - } -} \ No newline at end of file diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/WaveController.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/WaveController.java index cd19c30..a14c907 100644 --- a/rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/WaveController.java +++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/WaveController.java @@ -111,13 +111,11 @@ ExcelUtil.build(ExcelUtil.create(waveService.list(), Wave.class), response); } - - @PreAuthorize("hasAuthority('manager:wave:update')") @ApiOperation("娉㈡涓嬪彂浠诲姟") @PostMapping("/wave/public/task") public R publicTask(@RequestBody Map<String, Object> map) { - if (Cools.isEmpty(map)) { + if (Cools.isEmpty(map) || Cools.isEmpty(map.get("wave"))) { throw new CoolException("鍙傛暟涓嶈兘涓虹┖锛侊紒"); } return waveService.publicTask(map); @@ -127,7 +125,7 @@ @ApiOperation("娉㈡鍑哄簱浠诲姟棰勮") @PostMapping("/wave/locs/preview/page") public R mergeWavePreview(@RequestBody Map<String, Object> map) { - if (Cools.isEmpty(map.get("waveId")) || StringUtils.isBlank(map.get("waveId").toString())) { + if (Cools.isEmpty(map.get("wave")) || StringUtils.isBlank(map.get("wave").toString())) { throw new CoolException("鍙傛暟涓嶈兘涓虹┖锛侊紒"); } Long waveId = Long.parseLong(map.get("waveId").toString()); diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/WaveItem.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/WaveItem.java index ee1a48c..619bc5f 100644 --- a/rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/WaveItem.java +++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/WaveItem.java @@ -170,11 +170,21 @@ @ApiModelProperty(value= "淇敼浜哄憳") private Long updateBy; + /** + * 鐩爣搴撲綅 + */ @ApiModelProperty("鐩爣搴撲綅") @TableField(exist = false) private String stockLocs; /** + * 搴撳瓨鏁伴噺 + */ + @ApiModelProperty("搴撳瓨鏁伴噺") + @TableField(exist = false) + private Double stockQty; + + /** * 澶囨敞 */ @ApiModelProperty(value= "澶囨敞") diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/WaveServiceImpl.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/WaveServiceImpl.java index 111ac2f..96355b3 100644 --- a/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/WaveServiceImpl.java +++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/WaveServiceImpl.java @@ -17,15 +17,14 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.vincent.rsf.server.manager.utils.OptimalAlgorithmUtil; import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Objects; +import java.util.*; import java.util.stream.Collectors; +import java.util.stream.IntStream; @Service("waveService") public class WaveServiceImpl extends ServiceImpl<WaveMapper, Wave> implements WaveService { @@ -53,10 +52,11 @@ @Override @Transactional(rollbackFor = Exception.class) public R publicTask(Map<String, Object> map) { - List<Long> ids = (List<Long>) map.get("ids"); - if (Objects.isNull(ids) || ids.isEmpty()) { + List<WaveItem> itemParams = (List<WaveItem>) map.get("wave"); + if (Objects.isNull(itemParams) || itemParams.isEmpty()) { throw new CoolException("鍙傛暟涓嶈兘涓虹┖锛侊紒"); } + List<Long> ids = itemParams.stream().map(WaveItem::getWaveId).collect(Collectors.toList()); List<Wave> waves = this.list(new LambdaQueryWrapper<Wave>().in(Wave::getId, ids)); if (Objects.isNull(waves) || waves.isEmpty()) { throw new CoolException("娉㈡鏁版嵁涓嶅瓨鍦紒锛�"); @@ -67,10 +67,9 @@ throw new CoolException("娉㈡鏄庣粏涓嶅瓨鍦紒锛�"); } List<Long> orderIds = waveItems.stream().map(WaveItem::getOrderId).collect(Collectors.toList()); - /**鏌ヨ姣忔潯鏄庣粏鍖归厤鐨勫簱浣�*/ try { - List<WaveItem> items = getLocs(waveItems); +// List<WaveItem> items = getLocs(waveItems); } catch (Exception e) { throw new CoolException("搴撲綅鑾峰彇澶辫触锛侊紒锛�"); } @@ -78,16 +77,16 @@ // 2. 鏍规嵁鐗╂枡SKU瀵绘壘绗﹀悎鐗╂枡搴撲綅 {1. 鏍规嵁鐗╂枡缂栫爜锛屾壒娆★紝鍔ㄦ�佸瓧娈� 鏌ヨ绗﹀悎鐨勫簱浣嶏紝鍐嶆牴鎹簱浣嶄腑鐗╂枡鐨勬暟閲忛�夋嫨鏈�閫傚悎鐨勫簱浣� 2. 鍒ゆ柇褰撳墠璁㈠崟鏄叏鎷栧嚭搴撹繕鏄嫞鏂欏叆搴搣 // 3. 淇敼涓诲崟銆佹尝娆℃墽琛屾暟閲� // 4. 鍒ゆ柇鍏ㄤ粨鍑哄簱鎴栨嫞鏂欏嚭搴� - List<AsnOrder> orders = asnOrderService.list(new LambdaQueryWrapper<AsnOrder>().in(AsnOrder::getId, orderIds)); - /**淇敼鍘熷嚭搴撳崟鐘舵��*/ - if (!asnOrderService.update(new LambdaQueryWrapper<AsnOrder>() - .eq(AsnOrder::getExceStatus, AsnExceStatus.OUT_STOCK_STATUS_TASK_WORKING.val) - .in(AsnOrder::getId, orders))) { - throw new CoolException("鍑哄簱鍗曟嵁鐘舵�佷慨鏀瑰け璐ワ紒锛�"); - } - if (!this.update(new LambdaUpdateWrapper<Wave>().set(Wave::getExceStatus, WaveExceStatus.WAVE_EXCE_STATUS_TASK).in(Wave::getId, ids))) { - throw new CoolException("娉㈡鐘舵�佷慨鏀瑰け璐ワ紒锛�"); - } +// List<AsnOrder> orders = asnOrderService.list(new LambdaQueryWrapper<AsnOrder>().in(AsnOrder::getId, orderIds)); +// /**淇敼鍘熷嚭搴撳崟鐘舵��*/ +// if (!asnOrderService.update(new LambdaQueryWrapper<AsnOrder>() +// .eq(AsnOrder::getExceStatus, AsnExceStatus.OUT_STOCK_STATUS_TASK_WORKING.val) +// .in(AsnOrder::getId, orders))) { +// throw new CoolException("鍑哄簱鍗曟嵁鐘舵�佷慨鏀瑰け璐ワ紒锛�"); +// } +// if (!this.update(new LambdaUpdateWrapper<Wave>().set(Wave::getExceStatus, WaveExceStatus.WAVE_EXCE_STATUS_TASK).in(Wave::getId, ids))) { +// throw new CoolException("娉㈡鐘舵�佷慨鏀瑰け璐ワ紒锛�"); +// } return R.ok(); } @@ -136,15 +135,26 @@ .eq(LocItem::getSplrBatch, waveItem.getSplrBatch()) .eq(StringUtils.isNotBlank(waveItem.getFieldsIndex()), LocItem::getFieldsIndex, waveItem.getFieldsIndex()) .groupBy(LocItem::getMatnrCode, LocItem::getSplrBatch, LocItem::getFieldsIndex, LocItem::getId)); + List<Double> doubles1 = locItems.stream().map(LocItem::getAnfme).collect(Collectors.toList()); + double[] doubles = doubles1.stream().mapToDouble(Double::doubleValue).toArray(); - Double[] doubles = locItems.stream().map(LocItem::getAnfme).toArray(Double[]::new); - List<Double> result = OptimalAlgorithmUtil.findBestCombination(doubles, waveItem.getAnfme()); - String locs = JSONArray.toJSONString(new ArrayList<>()); - if (!locItems.isEmpty()) { - List<String> codes = locItems.stream().map(LocItem::getLocCode).collect(Collectors.toList()); - locs = JSONArray.toJSONString(codes); + /**浣跨敤鍥炴函绠楁硶璁$畻锛岃幏鍙栫鍚堝嚭搴撻噺鐨勬渶绠�缁勫悎*/ + List<Integer> result = OptimalAlgorithmUtil.findCombination(doubles, waveItem.getAnfme()); + String locs = "[]"; + if (Objects.isNull(result)) { + waveItem.setStockLocs(locs).setStockQty(0.0); + } else { + /**杩囨护闆嗗悎涓渶绠�鐭殑缁勫悎*/ + List<LocItem> locsInfo = result.stream() + .filter(i -> i >= 0 && i < locItems.size()) + .map(locItems::get).collect(Collectors.toList()); + locs = JSONArray.toJSONString(locsInfo); + double sumQty = locItems.stream().mapToDouble(LocItem::getAnfme).sum(); + double surQty = locItems.stream().mapToDouble(LocItem::getWorkQty).sum(); + double qty = locItems.stream().mapToDouble(LocItem::getQty).sum(); + double v = sumQty - surQty - qty; + waveItem.setStockLocs(locs).setStockQty(v); } - waveItem.setStockLocs(locs); }); return waveItems; } diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/manager/utils/OptimalAlgorithmUtil.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/utils/OptimalAlgorithmUtil.java index 7ada63a..a3704cb 100644 --- a/rsf-server/src/main/java/com/vincent/rsf/server/manager/utils/OptimalAlgorithmUtil.java +++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/utils/OptimalAlgorithmUtil.java @@ -6,9 +6,9 @@ import java.util.stream.Collectors; /** + * @param * @author Ryan * @description 鏍规嵁瀵硅薄澶氫釜灞炴�у悎骞跺鐞� - * @param * @return * @time 2025/4/25 10:55 */ @@ -46,9 +46,9 @@ } /** + * @param * @author Ryan * @description 鐢ㄤ簬瀛樺偍澶氫釜鍒嗙粍閿殑鍐呴儴绫� - * @param * @return * @time 2025/4/25 10:56 */ @@ -80,52 +80,140 @@ } - public static List<Double> findBestCombination(Double[] candidates, double targetSum) { - bestSolution = null; - target = targetSum; - minDifference = Double.MAX_VALUE; - Arrays.sort(candidates); - backtrack(candidates, 0, new ArrayList<>(), 0.0); - - // 濡傛灉娌℃湁绮剧‘瑙o紝杩斿洖鏈�鎺ヨ繎鐨勮繎浼艰В - return bestSolution != null ? bestSolution : new ArrayList<>(); - } - - private static void backtrack(Double[] candidates, int start, - List<Double> current, double currentSum) { - // 璁$畻褰撳墠鍜屼笌鐩爣鐨勫樊鍊� - double difference = Math.abs(currentSum - target); - - // 濡傛灉鎵惧埌鏇翠紭瑙o紙宸�兼洿灏忔垨宸�肩浉鍚屼絾缁勫悎鏇寸煭锛� - if (difference < minDifference - EPSILON || - (Math.abs(difference - minDifference) < EPSILON && - (bestSolution == null || current.size() < bestSolution.size()))) { - minDifference = difference; - bestSolution = new ArrayList<>(current); + /** + * @param + * @return + * @author Ryan + * @description 鑾峰彇鍏ㄦ嫋鎴栭潪鍏ㄦ嫋搴撲綅 + * @time 2025/4/28 09:41 + */ + public static List<Integer> findCombination(double[] nums, Double target) { + // 澶勭悊绛夊�兼儏鍐� + List<Integer> equal = findEqual(nums, target); + if (equal != null && !equal.isEmpty()) { + return equal; } - // 濡傛灉宸茬粡鎵惧埌绮剧‘瑙o紝涓嶉渶瑕佺户缁悳绱㈡洿闀跨殑缁勫悎 - if (minDifference < EPSILON) { + // 澶勭悊澶т簬鐨勬儏鍐� + List<Integer> greater = findGreater(nums, target); + if (greater != null && !greater.isEmpty()) { + return greater; + } + + // 澶勭悊灏忎簬鐨勬儏鍐� + List<Integer> less = findLess(nums, target); + return less != null ? less : new ArrayList<>(); // 纭繚涓嶈繑鍥瀗ull + } + + /** + * @author Ryan + * @description 浣跨敤鍔ㄦ�佽鍒掑鎵惧拰绛変簬鐩爣鍊肩殑鏈�灏戝厓绱犵粍鍚堛�傚姩鎬佽鍒掓暟缁刣p璁板綍杈惧埌姣忎釜鍜屾墍闇�鐨勬渶灏戝厓绱犳暟鐩紝prev鏁扮粍璁板綍璺緞浠ヤ究鍥炴函鎵惧埌鍏蜂綋绱㈠紩銆� + * @param + * @return + * @time 2025/4/28 12:33 + */ + private static List<Integer> findEqual(double[] nums, double target) { + int n = nums.length; + double[] dp = new double[(int) (target * 100 + 1)]; // 鏀惧ぇ100鍊嶅鐞嗙簿搴﹂棶棰� + Arrays.fill(dp, Double.MAX_VALUE); + dp[0] = 0; + int[] prev = new int[(int) (target * 100 + 1)]; + Arrays.fill(prev, -1); + + for (int j = 0; j < n; j++) { + int num = (int) (nums[j] * 100); // 鏀惧ぇ100鍊嶅鐞嗙簿搴﹂棶棰� + for (int i = (int) (target * 100); i >= num; i--) { + if (dp[i - num] != Double.MAX_VALUE && dp[i - num] + 1 < dp[i]) { + dp[i] = dp[i - num] + 1; + prev[i] = j; + } + } + } + + if (dp[(int) (target * 100)] == Double.MAX_VALUE) { + return null; + } + + List<Integer> indices = new ArrayList<>(); + int current = (int) (target * 100); + while (current > 0) { + int j = prev[current]; + if (j == -1) return null; + indices.add(j); + current -= (int) (nums[j] * 100); + } + + return indices; + } + + /** + * @author Ryan + * @description 灏嗘暟缁勯檷搴忔帓搴忓悗锛岃椽蹇冨湴绱姞鍏冪礌鐩村埌鎬诲拰瓒呰繃鐩爣鍊硷紝杩斿洖杩欎簺鍏冪礌鐨勭储寮� + * @param + * @return + * @time 2025/4/28 12:34 + */ + private static List<Integer> findGreater(double[] nums, double target) { + List<double[]> sorted = new ArrayList<>(); + for (int i = 0; i < nums.length; i++) { + sorted.add(new double[]{nums[i], i}); + } + sorted.sort((a, b) -> Double.compare(b[0], a[0])); + + double sum = 0; + List<Integer> indices = new ArrayList<>(); + for (double[] pair : sorted) { + sum += pair[0]; + indices.add((int) pair[1]); + if (sum > target) { + return indices; + } + } + + return null; + } + + /** + * @author Ryan + * @description 浣跨敤鍥炴函娉曠敓鎴愭墍鏈夊彲鑳界殑缁勫悎锛屽鎵炬�诲拰鏈�澶т絾涓嶈秴杩囩洰鏍囧�肩殑缁勫悎锛屽苟璁板綍鍏冪礌鏁扮洰鏈�灏戠殑缁勫悎銆� + * @param + * @return + * @time 2025/4/28 12:34 + */ + private static List<Integer> findLess(double[] nums, double target) { + int n = nums.length; + List<Integer> bestIndices = new ArrayList<>(); + double[] maxSum = {-1.0}; + for (int k = 1; k <= n; k++) { + List<Integer> currentIndices = new ArrayList<>(); + double[] currentMax = {-1.0}; + backtrack(nums, target, 0, k, 0.0, 0, currentIndices, currentMax, new ArrayList<>()); + if (currentMax[0] != -1.0) { + if (currentMax[0] > maxSum[0] || (currentMax[0] == maxSum[0] && currentIndices.size() < bestIndices.size())) { + maxSum[0] = currentMax[0]; + bestIndices = new ArrayList<>(currentIndices); + } + } + } + + return bestIndices.isEmpty() ? null : bestIndices; + } + + private static void backtrack(double[] nums, double target, int start, int k, double currentSum, int count, List<Integer> currentIndices, double[] currentMax, List<Integer> path) { + if (count == k) { + if (currentSum <= target && currentSum > currentMax[0]) { + currentMax[0] = currentSum; + currentIndices.clear(); + currentIndices.addAll(path); + } return; } - // 閬嶅巻鍊欓�夋暟瀛� - for (int i = start; i < candidates.length; i++) { - double num = candidates[i]; - - // 鍓灊锛氬鏋滃綋鍓嶅拰宸茬粡杩滃ぇ浜庣洰鏍囧�硷紝璺宠繃 - if (currentSum + num > target + minDifference + EPSILON) { - continue; - } - - // 璺宠繃閲嶅鏁板瓧 - if (i > start && Math.abs(candidates[i] - candidates[i - 1]) < EPSILON) { - continue; - } - - current.add(num); - backtrack(candidates, i + 1, current, currentSum + num); - current.remove(current.size() - 1); + for (int i = start; i < nums.length; i++) { + if (currentSum + nums[i] > target) continue; + path.add(i); + backtrack(nums, target, i + 1, k, currentSum + nums[i], count + 1, currentIndices, currentMax, path); + path.remove(path.size() - 1); } } -- Gitblit v1.9.1