项目排程问题
假设你是Boffin的PM,目前正同时管理A,B和C三个项目,这3个小牧都需要先有翻译部门完成翻译后, 再交至DTP部门进行排版,如果翻译部门及DTP部门的全部产能安排在单个项目上,各个项目的翻译及 版权所需工期如下表。请规划三个项目处理次序,使3个项目的总工期最短(即,第一个项目翻译开始 和最后一个项目DTP结束的时间最短)
要找出最短工时组合我们需要解决两个问题:
- 自由组合所有项目
- 计算每种组合所需的工时
自由组合所有项目
假设有数组[‘A’,’B’] 则数组中元素自由组合结果为[‘A’,’B’]和[‘B’,’A’]。
假设我们有数组[‘A’,’B’,’C’],我们可以将之看为 ’A’与数组[‘B’,’C’]的组合,’B’ 与数组[‘A’,’C’]的组合和‘C’与数组[‘A’,’B’]的组合,结果为:
[‘A’,’B’,’C’] [‘A’,’C’,’B’]
[‘B’,’A’,’C’] [‘B’,’C’,’A’]
[‘C’,’A’,’B’] [‘C’,’B’,’A’]
假设我们有数组[P1,P2,P3,…,Pn] 就可以分解为P1与数组[P2,P3,…,Pn]组合,而数组[P2,P3,…,Pn]的组合又可以看作P2与[P3,…,Pn]的组合,依次类推,我们就可以得到以下代码:
/**
* 数组中的元素自由组合
* @param {*} array
*
* 思路
* 考虑序列 [P1,P2,P3,...,Pn] 可以分解为 P1 与 C([P2,P3,...,Pn])的结果组合,依此类推.
*/
function combination(array){
var newArray =new Array();
if(array.length===1)
{
newArray.push(array[0]);
return newArray;
}
if(array.length===2){
newArray.push([array[0],array[1]]);
newArray.push([array[1],array[0]]);
return newArray;
}
for(var i=0;i<array.length;i++){
var copyArray = array.slice(0);
var item=copyArray.splice(i,1);
var nna=combination(copyArray);
for(var j=0;j<nna.length;j++){
newArray.push(item.concat(nna[j]));
}
}
return newArray;
}计算组合所需总工时
设我们有排程ABC。
在不考虑AB排版时间的情况下,需要用时应该为: 理想时间= A.翻译时间+B.翻译时间+C.翻译时间+C.排版时间。
但是在有些情况下由于AB排版所需时间太长,导致C排版需要等待。
所以 排程ABC实际用时=理想时间+C排版等待时间。
这时我们需要计算C排版等待时间。
C排版等待时间应该等于B排版总用时-C翻译时间。
B排版总用时应该等于B排版等待时间+B排版时间。
B排版等待时间应该等于A排版时间-B翻译时间。
所以就有以下代码:
/**
* 排程实际时间
* 实际时间=理想时间+等待时间
*/
this.js = function(data) {
var lixiangTime = this.lixiangTime(data);
var dengdaiTime = this.dengdaiTime(data);
var obj = {
key: "",
lixiangTime: lixiangTime,
dengdaiTime: dengdaiTime,
totalTime: lixiangTime + dengdaiTime
}
for (var i = 0; i < data.length; i++) {
obj.key += data[i].name;
}
return obj;
};
/**
* 理想时间
* 设排程ABC
* 理想时间=A.翻译+B.翻译+C.翻译+C.排版
*/
this.lixiangTime = function(data) {
var time = 0;
for (var i = 0; i < data.length; i++) {
if (i === data.length - 1)
return time += parseInt(data[i].paiban) + parseInt(data[i].fanyi);
time += parseInt(data[i].fanyi);
}
};
/***
* 翻译等待时间
* 设排程ABC
* B排版等待时间=A.排版时间-B.翻译时间
* C排版等待时间=B排版时间+B排版等待时间-C翻译时间
* 时间不能小于0
*/
this.dengdaiTime = function(data) {
var time = 0;
for (var i = 1; i < data.length; i++) {
time = parseInt(time) + parseInt(data[i - 1].paiban) - parseInt(data[i].fanyi);
time = time < 0 ? 0 : time;
}
return time;
};完整代码
HTML 代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>项目排程</title>
<link href="css/index.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div>
假设你是Boffin的PM,目前正同时管理A,B和C三个项目,这3个小牧都需要先有翻译部门完成翻译后, 再交至DTP部门进行排版,如果翻译部门及DTP部门的全部产能安排在单个项目上,各个项目的翻译及 版权所需工期如下表。请规划三个项目处理次序,使3个项目的总工期最短(即,第一个项目翻译开始 和最后一个项目DTP结束的时间最短)
</div>
<div data-bind="with:newItem">
<div>
<label for="projectName">项目名称:</label>
<input type="text" id="projectName" data-bind="value:name" />
</div>
<div>
<label for="fanyi">翻译用时:</label>
<input type="text" id="fanyi" data-bind="value:fanyi" />
</div>
<div>
<label for="paiban">排版用时:</label>
<input type="text" id="paiban" data-bind="value:paiban" />
</div>
<div>
<button data-bind="click:$parent.addItem">新增</button>
</div>
</div>
<div>
<table>
<thead>
<tr>
<th>项目名称</th>
<th>翻译用时</th>
<th>排版用时</th>
<th>操作</th>
</tr>
</thead>
<tbody data-bind="foreach: allItems">
<tr>
<th><label for="" data-bind="text:name"></label></td>
<td><label for="" data-bind="text:fanyi"></label></td>
<td><label for="" data-bind="text:paiban"></label></td>
<td><a href='#' data-bind='click: $root.removeItem'>删除</a></td>
</tr>
</tbody>
</table>
<label for="" data-bind="text:allItems().length"></label>
<br>
<button data-bind="click:jisuan">计算</button>
</div>
<div>
日志:
<div id="log" data-bind="foreach:allLogs">
<span data-bind="text:$data"></span>
</div>
</div>
<script src="js/knockout-3.5.1.js" type="text/javascript"></script>
<script src="js/index.js" type="text/javascript"></script>
</body>
</html>JS 代码:
var listModel = function(items) {
var self = this;
this.newItem = new function() {
this.name = ko.observable("");
this.fanyi = ko.observable("");
this.paiban = ko.observable("");
this.toJson = function() {
return JSON.parse(ko.toJSON(this));
};
this.clear = function() {
this.name("");
this.fanyi("");
this.paiban("");
};
};
this.allItems = ko.observableArray(items);
this.allLogs = ko.observableArray([]);
this.addItem = function() {
var data = this.newItem.toJson();
console.log(data);
if (this.allItems.indexOf(data) < 0) {
this.allItems.push(data);
this.newItem.clear();
}
}.bind(this);
this.removeItem = function() {
self.allItems.remove(this);
};
/**
* 自由组合所有项目
* 并在组合中找出用时最少组合
*/
this.jisuan = function() {
this.addLog("开始计算。。。");
var data = this.allItems();
var bestTime;
data = combination(data);
for (var i = 0; i < data.length; i++) {
var time = this.js(data[i]);
this.addLog("排列顺序:" + time.key + " 理想时间:" + time.lixiangTime + " 等待时间:" + time.dengdaiTime + " 实际时间:" + time.totalTime);
if (!bestTime)
bestTime = time;
else if (bestTime.totalTime > time.totalTime) {
bestTime = time;
}
}
this.addLog("最优排列顺序:" + bestTime.key + " 理想时间:" + bestTime.lixiangTime + " 等待时间:" + bestTime.dengdaiTime + " 实际时间:" + bestTime.totalTime)
this.addLog("开始结束。。。");
}.bind(this);
this.addLog = function(mesage) {
this.allLogs.push(mesage);
var log = document.querySelector("#log")
log.scrollTop = log.scrollHeight;
};
/**
* 排程实际时间
* 实际时间=理想时间+等待时间
*/
this.js = function(data) {
var lixiangTime = this.lixiangTime(data);
var dengdaiTime = this.dengdaiTime(data);
var obj = {
key: "",
lixiangTime: lixiangTime,
dengdaiTime: dengdaiTime,
totalTime: lixiangTime + dengdaiTime
}
for (var i = 0; i < data.length; i++) {
obj.key += data[i].name;
}
return obj;
};
/**
* 理想时间
* 设排程ABC
* 理想时间=A.翻译+B.翻译+C.翻译+C.排版
*/
this.lixiangTime = function(data) {
var time = 0;
for (var i = 0; i < data.length; i++) {
if (i === data.length - 1)
return time += parseInt(data[i].paiban) + parseInt(data[i].fanyi);
time += parseInt(data[i].fanyi);
}
};
/***
* 翻译等待时间
* 设排程ABC
* B排版等待时间=A.排版时间-B.翻译时间
* C排版等待时间=B排版时间+B排版等待时间-C翻译时间
* 时间不能小于0
*/
this.dengdaiTime = function(data) {
var time = 0;
for (var i = 1; i < data.length; i++) {
time = parseInt(time) + parseInt(data[i - 1].paiban) - parseInt(data[i].fanyi);
time = time < 0 ? 0 : time;
}
return time;
};
}
var items = [{
name: "A",
fanyi: 20,
paiban: 13
}, {
name: "B",
fanyi: 4,
paiban: 10
}, {
name: "C",
fanyi: 13,
paiban: 3
}];
ko.applyBindings(new listModel(items));
/**
* 数组中的元素自由组合
* @param {*} array
*
* 思路
* 考虑序列 [P1,P2,P3,...,Pn] 可以分解为 P1 与 C([P2,P3,...,Pn])的结果组合,依此类推.
*/
function combination(array) {
var newArray = new Array();
if (array.length === 1) {
newArray.push(array[0]);
return newArray;
}
if (array.length === 2) {
newArray.push([array[0], array[1]]);
newArray.push([array[1], array[0]]);
return newArray;
}
for (var i = 0; i < array.length; i++) {
var copyArray = array.slice(0);
var time = copyArray.splice(i, 1);
var nna = combination(copyArray);
for (var j = 0; j < nna.length; j++) {
newArray.push(time.concat(nna[j]));
}
}
return newArray;
}css代码:
div {
margin: 10px;
}
#log {
border-style: ridge;
border-width: 1px;
border-color: cadetblue;
min-height: 160px;
max-height: 400px;
overflow-y: scroll;
}
table {
border-collapse: collapse;
}
table thead tr {
background-color: #34495e;
}
table tbody tr:nth-child(odd) {
background-color: #95a5a6;
}
table tbody tr:nth-child(even) {
background-color: #7f8c8d;
}
table th {
min-width: 80px;
border-width: 1px;
border-style: ridge;
border-color: #130f40;
}
table tr td {
border-width: 1px;
border-style: ridge;
border-color: #34495e;
text-align: center;
}
#log span {
display: block;
margin: 5px;
}思考
该项目只有两个工序,倘若是N个工序的项目,该如何计算排程用时。


浙公网安备 33010602011771号