МІНІСТЕРСТВО ОСВІТИ УКРАЇНИ
НАЦІОНАЛЬНИЙ УНІВЕРСИТЕТ “ЛЬВІВСЬКА ПОЛІТЕХНІКА”
КАФЕДРА ПРОГРАМНОГО ЗАБЕЗПЕЧЕННЯ
Звіт
До лабораторної роботи № 2
На тему: “ Побудова розкладу роботи обладнання виробничої системи ”
З дисципліни : "Моделювання програмного забезпечення"
Мета роботи: побудувати розклад роботи обладнання виробничої системи.
Завдання:
Виробнича система складає m верстатів, настроєних на виконання певної технологічної операції. На обробку надходить n партій заготовок з однаковими технологічними маршрутами, що передбачають обробку заготовок на всіх m верстатах. Для заданих у варіанті значень m і n, а також матриці T=tij нормативних значень часу на виконання операцій побудувати розклад роботи обладнання виробничої системи двома способами:
а) повним перебором всіх можливих послідовностей виконання робіт, що надійшли на обробку;
б) за алгоритмом Дудека.
Розклад оптимізувати за критерієм мінімуму затрат часу на виконання всіх робіт.
Код програми
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace Lab2_MPZ
{
public partial class Form1 : Form
{
public int[,] InputTimeAllocation;
public int[][] Sequence;
public class TimePosition
{
public int Value;
public int Row;
public int Column;
public TimePosition(int Time, int x, int y)
{
Value = Time;
Row = x;
Column = y;
}
}
public class TimePositionComparer : IComparer<TimePosition>
{
#region IComparer<TimePosition> Members
public int Compare(TimePosition x, TimePosition y)
{
if (x.Value == y.Value)
return 0;
if (x.Value < y.Value)
return -1;
if (x.Value > y.Value)
return 1;
return x.ToString().CompareTo(y.ToString());
}
#endregion
}
public Form1()
{
InitializeComponent();
dataGridView1.Rows.Add();
dataGridView1.Rows.Add();
dataGridView1.Rows.Add();
dataGridView1.Rows.Add();
dataGridView1.Rows[0].Cells[0].Value = "14";
dataGridView1.Rows[0].Cells[1].Value = "8";
dataGridView1.Rows[0].Cells[2].Value = "3";
dataGridView1.Rows[0].Cells[3].Value = "1";
dataGridView1.Rows[0].Cells[4].Value = "2";
dataGridView1.Rows[1].Cells[0].Value = "3";
dataGridView1.Rows[1].Cells[1].Value = "6";
dataGridView1.Rows[1].Cells[2].Value = "10";
dataGridView1.Rows[1].Cells[3].Value = "5";
dataGridView1.Rows[1].Cells[4].Value = "1";
dataGridView1.Rows[2].Cells[0].Value = "1";
dataGridView1.Rows[2].Cells[1].Value = "4";
dataGridView1.Rows[2].Cells[2].Value = "6";
dataGridView1.Rows[2].Cells[3].Value = "3";
dataGridView1.Rows[2].Cells[4].Value = "3";
dataGridView1.Rows[3].Cells[0].Value = "6";
dataGridView1.Rows[3].Cells[1].Value = "20";
dataGridView1.Rows[3].Cells[2].Value = "3";
dataGridView1.Rows[3].Cells[3].Value = "5";
dataGridView1.Rows[3].Cells[4].Value = "6";
InputTimeAllocation = new int[dataGridView1.RowCount - 1, dataGridView1.ColumnCount];
}
private void DudecCount_Click(object sender, EventArgs e)
{
InputTimeAllocation = new int[dataGridView1.RowCount - 1, dataGridView1.ColumnCount];
// зчитування даних з таблиці
for (int i = 0; i < dataGridView1.RowCount - 1; i++)
for (int j = 0; j < dataGridView1.ColumnCount; j++)
try
{
InputTimeAllocation[i, j] = int.Parse((string)dataGridView1.Rows[i].Cells[j].Value);
}
catch
{
MessageBox.Show(string.Format("Помилка при вводі даних в комірку ({0}, {1})", i, j));
return;
}
int MachineCount = InputTimeAllocation.GetLength(1);
int DetailCount = InputTimeAllocation.GetLength(0);
List<Point>[] JonsonInput = new List<Point>[MachineCount - 1];
for (int i = 0; i < (MachineCount - 1); i++)
JonsonInput[i] = new List<Point>();
// генеруємо вхідні дані для задачі Джонсона з двома верстатами
for (int i = 0; i < (MachineCount - 1); i++)
for (int j = 0; j < DetailCount; j++)
{
int A = 0;
int B = 0;
for (int k = 0; k <= i; k++)
{
A += InputTimeAllocation[j, k];
B += InputTimeAllocation[j, MachineCount - k - 1];
}
JonsonInput[i].Add(new Point(A, B));
}
#region Метод Джонсона для двох машин для MachineCount-1 випадків
int OptionIndex = 0;
int[][] result = new int[MachineCount - 1][];
int[] result_time = new int[MachineCount - 1];
for (int i = 0; i < (MachineCount - 1); i++)
{
result[i] = new int[DetailCount];
}
do
{
List<TimePosition> TimeAllocation = new List<TimePosition>();
for (int i = 0; i < DetailCount; i++)
{
TimeAllocation.Add(new TimePosition(JonsonInput[OptionIndex][i].X, i, 0));
TimeAllocation.Add(new TimePosition(JonsonInput[OptionIndex][i].Y, i, 1));
}
TimeAllocation.Sort(new TimePositionComparer());
bool[] used = new bool[DetailCount];
List<int>[] result_tmp = new List<int>[2];
result_tmp[0] = new List<int>();
result_tmp[1] = new List<int>();
for (int i = 0; i < 2 * DetailCount; i++)
{
if (!used[TimeAllocation[i].Row])
{
used[TimeAllocation[i].Row] = true;
result_tmp[TimeAllocation[i].Column].Add(TimeAllocation[i].Row);
}
}
result_tmp[0].CopyTo(result[OptionIndex]);
result_tmp[1].Reverse();
result_tmp[1].CopyTo(result[OptionIndex], result_tmp[0].Count);
#region Обрахунок часу роботи лінії
int timeA = 0, timeB = 0;
for (int i = 0; i < DetailCount; i++)
{
int curr_pos = result[OptionIndex][i];
timeA += JonsonInput[OptionIndex][curr_pos].X;
if (timeB < timeA)
timeB = timeA;
timeB += JonsonInput[OptionIndex][curr_pos].Y;
}
if (timeA > timeB)
result_time[OptionIndex] = timeA;
else
result_time[OptionIndex] = timeB;
#endregion
OptionIndex++;
}
while (OptionIndex < (MachineCount - 1));
#endregion
int MinTime = int.MaxValue;
int MinIndex = 0;
for (int i = 0; i < result_time.Length; i++)
{
if (result_time[i] < MinTime)
{
MinTime = result_time[i];
MinIndex = i;
}
}
MinTime = WorkingTimeCounting(InputTimeAllocation, result[MinIndex]);
label1.Text = string.Format("Мінімальні затрати по часу для обробки всіх деталей складуть {0} сек\r\nпри такій послідовності обробки деталей:\r\n", MinTime);
foreach (int t in result[MinIndex])
label1.Text = label1.Text + string.Format("{0}, ", (t + 1).ToString());
Sequence = new int[result.GetLength(0)][];
listBox2.Items.Clear();
for (int i = 0; i < result.Length; i++)
{
Sequence[i] = result[i];
StringBuilder sb = new StringBuilder();
foreach (int t in result[i])
sb.AppendFormat("{0}, ", t+1);
sb.AppendFormat(" {0} сек", WorkingTimeCounting(InputTimeAllocation, result[i]));
listBox2.Items.Add(sb.ToString());
}
/*
MachineCount = 2;
int[,] DrawInput = new int[DetailCount, MachineCount];
for (int i = 0; i < DetailCount; i++)
{
DrawInput[i, 0] = JonsonInput[MinIndex][i].X;
DrawInput[i, 1] = JonsonInput[MinIndex][i].Y;
}
*/
WorkGraphicDrawing(InputTimeAllocation, result[MinIndex], MinTime, pictureBox1);
}
public int WorkingTimeCounting(int[,] InputTimeAllocation, int[] Sequence)
{
#region Обрахунок часу роботи лінії для основної задачі
int DetailCount = InputTimeAllocation.GetLength(0);
int MachineCount = InputTimeAllocation.GetLength(1);
int[] Time = new int[MachineCount]; // загальний час роботи кожного верстата
int time;
for (int i = 0; i < DetailCount; i++)
for (int j = 0; j < MachineCount; j++)
{
time = InputTimeAllocation[Sequence[i], j];
Time[j] += time;
for (int k = j + 1; k < MachineCount; k++)
if (Time[j] - Time[k] > 0)
Time[k] += Time[j] - Time[k];
}
return Time.Max();
#endregion
}
public void WorkGraphicDrawing(int[,] DrawInput, int[] Sequence, double TotalTime, PictureBox PB)
{
#region малювання графіка завантженості
PB.Refresh();
// зчитуємо вихідні дані
Graphics gr = Graphics.FromHwnd(PB.Handle);
int X0 = 30;
int Y0 = PB.Height - 20;
int GraphicWidth = PB.Width - 20 - X0;
int GraphicHeigth = PB.Height - 20 - Y0;
int DetailCount = DrawInput.GetLength(0);
int MachineCount = DrawInput.GetLength(1);
// малюємо осі координат
gr.DrawLine(new Pen(Color.Black, 2), X0, 20, X0, Y0);
gr.DrawLine(new Pen(Color.Black, 2), X0, Y0, GraphicWidth + X0, Y0);
gr.DrawString("T", new Font("Arial", 14), Brushes.BlueViolet, X0 + GraphicWidth - 10, Y0 + 5);
// рахуємо висоту на якій буде знаходитися відповідна лінія кожного верстату
int[] HeightX = new int[MachineCount];
for (int i = 1; i <= MachineCount; i++)
{
HeightX[i - 1] = (PB.Height / (MachineCount + 1)) * i;
gr.DrawString("В " + i.ToString(), new Font("Arial", 10), Brushes.Fuchsia, X0 - 30, HeightX[i - 1] - 7);
}
// генеруємо кольори для кожної деталі
Color[] DetailsColor = new Color[DetailCount];
Random rand = new Random((int)DateTime.Now.Ticks);
for (int i = 0; i < DetailCount; i++)
{
DetailsColor[i] = new Color();
DetailsColor[i] = Color.FromArgb(rand.Next(255), rand.Next(255), rand.Next(255));
}
int[] MachineXPosition = new int[MachineCount]; // позиції графіків для кожного верстата
int[] MachineStandTime = new int[MachineCount]; // простої кожного верстата
int[] Time = new int[MachineCount]; // загальний час роботи кожного верстата
int time;
// обраховуємо масштаб графіка
double scale = (GraphicWidth-15) / TotalTime;
Pen DashPen = new Pen(Color.Silver, 2); // перо для пунктирної лінії
DashPen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
// малюємо сам графік
for (int i = 0; i < DetailCount; i++)
{
for (int k = 0; k < MachineCount; k++)
{
time = DrawInput[Sequence[i], k];
Time[k] += time;
for (int j = k + 1; j < MachineCount; j++)
{
if ((int)(time * scale) + MachineXPosition[k] - MachineXPosition[j] > 0)
{
MachineXPosition[j] += ((int)(time * scale) + MachineXPosition[k] - MachineXPosition[j]);
Time[j] += Time[k] - Time[j];
}
}
gr.DrawLine(new Pen(DetailsColor[Sequence[i]], 5), MachineXPosition[k] + X0 + (int)(MachineStandTime[k] * scale), HeightX[k],
MachineXPosition[k] + (int)(time * scale) + (int)(MachineStandTime[k] * scale) + X0, HeightX[k]);
gr.DrawLine(DashPen, MachineXPosition[k] + (int)(time * scale) + X0, HeightX[k],
MachineXPosition[k] + (int)(time * scale) + X0, Y0);
gr.DrawString(Time[k].ToString(), new Font("Arial", 10), new SolidBrush(DetailsColor[Sequence[i]]),
MachineXPosition[k] + (int)(time * scale) + X0 - 10, Y0 + 7);
MachineXPosition[k] += (int)(time * scale) + (int)(MachineStandTime[k] * scale);
}
}
#endregion
}
private void ColumnCounter_ValueChanged(object sender, EventArgs e)
{
if (dataGridView1.Columns.Count == ColumnCounter.Value)
return;
if (dataGridView1.Columns.Count < ColumnCounter.Value)
{
for (int i = dataGridView1.Columns.Count; i < ColumnCounter.Value; i++)
dataGridView1.Columns.Add("Column" + (i+1).ToString(), "");
}
if (dataGridView1.Columns.Count > ColumnCounter.Value)
{
for (int i = (int)ColumnCounter.Value; i < dataGridView1.Columns.Count; )
dataGridView1.Columns.Remove("Column" + dataGridView1.Columns.Count.ToString());
}
}
#region Ф-ції для генерації перестановок
private double Factor(double n)
{
if (n <= 1)
return 1;
else
return n * Factor(n - 1);
}
private void Swap(ref int a, ref int b)
{
int t;
t = a;
a = b;
b = t;
}
private void Reverse(ref int[] P, int m)
{
int i = 0, j = m;
while (i < j)
{
Swap(ref P[i], ref P[j]);
++i;
--j;
}
}
private void Antilex(ref int[] P, int m, ref int[][] Result, ref int index)
{
int i;
int N = P.GetLength(0);
if (m == 0)
{
for (i = 0; i < N; ++i)
Result[index][i] = P[i];
index++;
}
else
{
for (i = 0; i <= m; ++i)
{
Antilex(ref P, m - 1, ref Result, ref index);
if (i < m)
{
Swap(ref P[i], ref P[m]);
Reverse(ref P, m - 1);
}
}
}
}
#endregion
private void BruteForseMethod_Click(object sender, EventArgs e)
{
InputTimeAllocation = new int[dataGridView1.RowCount - 1, dataGridView1.ColumnCount];
// зчитування даних з таблиці
for (int i = 0; i < dataGridView1.RowCount - 1; i++)
for (int j = 0; j < dataGridView1.ColumnCount; j++)
try
{
InputTimeAllocation[i, j] = int.Parse((string)dataGridView1.Rows[i].Cells[j].Value);
}
catch
{
MessageBox.Show(string.Format("Помилка при вводі даних в комірку ({0}, {1})", i, j));
return;
}
int MachineCount = InputTimeAllocation.GetLength(1);
int DetailCount = InputTimeAllocation.GetLength(0);
int N = (int)Factor(DetailCount);
int[] P = new int[DetailCount];
for(int i=0; i<DetailCount; ++i)
P[i] = i;
Sequence = new int[N][];
for(int i=0; i<N; i++)
Sequence[i] = new int[DetailCount];
int index = 0;
Antilex(ref P, DetailCount - 1, ref Sequence, ref index);
int[] Time = new int[N];
int MinTime = int.MaxValue;
int MinIndex = 0;
for (int i = 0; i < N; i++)
{
Time[i] = WorkingTimeCounting(InputTimeAllocation, Sequence[i]);
if (Time[i] < MinTime)
{
MinTime = Time[i];
MinIndex = i;
}
}
label3.Text = string.Format("Мінімальні затрати по часу для обробки всіх деталей складуть {0} сек\r\nпри такій послідовності обробки деталей:\r\n", MinTime);
foreach (int t in Sequence[MinIndex])
label3.Text = label3.Text + string.Format("{0}, ", (t + 1).ToString());
WorkGraphicDrawing(InputTimeAllocation, Sequence[MinIndex], MinTime, pictureBox2);
listBox1.Items.Clear();
for (int i = 0; i < N; i++)
{
StringBuilder sb = new StringBuilder();
foreach(int t in Sequence[i])
sb.AppendFormat("{0}, ",t+1);
sb.AppendFormat(" {0} сек",WorkingTimeCounting(InputTimeAllocation,Sequence[i]));
listBox1.Items.Add(sb.ToString());
}
}
private void listBox1_Click(object sender, EventArgs e)
{
WorkGraphicDrawing(InputTimeAllocation, Sequence[listBox1.SelectedIndex], WorkingTimeCounting(InputTimeAllocation,Sequence[listBox1.SelectedIndex]), pictureBox2);
}
private void listBox2_Click(object sender, EventArgs e)
{
WorkGraphicDrawing(InputTimeAllocation, Sequence[listBox2.SelectedIndex],
WorkingTimeCounting(InputTimeAllocation, Sequence[listBox2.SelectedIndex]), pictureBox1);
}
}
}
Результат виконання завдання:
Висновок: в даній роботі я навчився будувати розклад роботи обладнання виробничої системи методами Дудека та повним перебором всіх можливих послідовностей виконання робіт, що надійшли на обробку. Обчислення проводились за такими вхідними даними: кількість верстатів, настроєних на виконання певної технологічної операції, кількість партій заготовок з однаковими технологічними маршрутами, що передбачають обробку заготовок на всіх m верстатах, а також матриці T=tij нормативних значень часу на виконання операцій.