【專案實戰】從抽獎演算法感受演算法奧妙
【前言】
最近因為公司年會需要,組長讓我們花幾天時間開發了個抽獎系統。雖然就是cs的一個小例子,不過自己設計演算法的過程還是蠻有意思的。下面先給大家看看,最後成品的樣子。主要實現的就是excel表的匯入匯出和抽獎功能, 這篇就講一下抽獎功能的實現。目前實現的效果是點選開始時,全屏隱去,只剩下名字在滾動顯示,需要停止時在datagridview中顯示出獲獎人資訊。
【設計思路】
首先從excel匯入名單到dataset,用timer 控制label(lblgetname)每毫秒顯示
一個dataset中的名字,迴圈顯示。 然後寫一個getname方法, 讓timer啟動一下,
系統延遲10毫秒,然後停一下,得到一個名字。 再寫一個getnewName(string[]
nameSet,string tmpName,int times)方法用來查重,將抽到的名字傳進去和
抽到的名字集合nameset做比較,有重複的,重新抽取。 SelectPerson就是呼叫get
name和getNewName獲得抽獎人,然後把已經抽到的人從dataSet中刪除,防止多次抽
獎重複抽取一個人。 這是這一版的實現思路,最終成型的和這個又有差別。
【正文】
最初的想法是通過隨機數來實現抽獎, 但是到多次抽獎間不重複的時候卡住了, 不知道怎麼讓隨機數產生時刨除確定的幾個數。
最後是通過label和timer實現的選人,每選一次就從dataSet中刪除一部分人。程式碼如下:
private void btnBegin_Click(object sender, EventArgs e)
{
label1.Visible = false;
label2.Visible = false;
label3.Visible = false;
txtNumber.Visible = false;
btnLook.Visible = false;
btnBegin.Visible = false;
btnPreserve.Visible = false;
btnResetting.Visible = false;
dgv.Visible = false;
lblgetName.Visible = true;
dgv.Rows.Clear();
if (user != "")
{
if (txtNumber.Text.Trim() != "")
{
lblgetName.Text = "吳迪";
if (ds.Tables[0].Rows.Count > int.Parse(txtNumber.Text) * 1.3)
{
timer1.Start();
btnBegin.Enabled = false;
}
else
{
MessageBox.Show("剩餘人數已不足");
lblgetName.Visible = false;
label1.Visible = true;
label2.Visible = true;
label3.Visible = true;
txtNumber.Visible = true;
btnLook.Visible = true;
btnBegin.Visible = true;
btnPreserve.Visible = true;
btnResetting.Visible = true;
dgv.Visible = true;
return;
}
}
else
{
MessageBox.Show("請輸入要選出的人數");
}
}
else
{
MessageBox.Show("請匯入人員資訊!");
}
}
private void lblgetName_Click(object sender, EventArgs e)
{
int number = int.Parse(txtNumber.Text.Trim());
string[] nameSet = selectPerson(number);
try
{
for (int rows = 0; rows <= ds.Tables[0].Rows.Count - 1; rows++)
{
for (int times = 0; times < number; times++)
{
if (ds.Tables[0].Rows[rows][1].ToString().Trim() == nameSet[times].ToString().Trim())
{
ds.Tables[0].Rows[rows][2] = "是";
ds.AcceptChanges();
}
}
}
}
catch (Exception)
{
MessageBox.Show("剩餘人數不足", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
List<LuckyPerson> luckyPeople = new List<LuckyPerson>();
int reCount = ds.Tables[0].Rows.Count - 1;
for (int rows = 0; rows <= reCount; rows++)
{
if (ds.Tables[0].Rows[rows][2].ToString().Trim() == "是")
{
LuckyPerson lucky = new LuckyPerson();
lucky.Name = ds.Tables[0].Rows[rows][1].ToString().Trim();
//2表示電話所在列
lucky.Tel = ds.Tables[0].Rows[rows][2].ToString().Trim();
luckyPeople.Add(lucky);
}
}
dgv.Rows.Add(luckyPeople.Count);
for (int i = 0; i < luckyPeople.Count; i++)
{
dgv.Rows[i].Cells[0].Value = luckyPeople[i].Name;
dgv.Rows[i].Cells[1].Value = luckyPeople[i].Tel;
}
deletLuky(reCount);
btnBegin.Enabled = true;
label1.Visible = true;
label2.Visible = true;
label3.Visible = true;
txtNumber.Visible = true;
btnLook.Visible = true;
btnBegin.Visible = true;
btnPreserve.Visible = true;
btnResetting.Visible = true;
dgv.Visible = true;
lblgetName.Visible = false;
}
#region 抽獎功能-宋榮凱-2017年3月14日
/// <summary>
///
//抽出幸運觀眾selectPerson-宋榮凱-2017年3月14日
/// </summary>
/// <param name="times">抽取數量</param>
/// <returns>獲獎人的集合</returns>
public String[] selectPerson(int times)
{
String[] nameSet = new String[times];
string nameTmp;
//迴圈將抽中人員放入集合中
for (int i = 0; i < times; i++)
{
nameTmp = getName();
if (i == 0)
{
nameSet[i] = nameTmp;
}
else
{
//檢查是否本次內是否有重複人員
nameSet[i] = getNewName(nameSet, nameTmp, i);
}
}
return nameSet;
}
/// <summary>
/// 若重複重新抽取--getNewName--宋榮凱-2017年3月14日
/// </summary>
/// <param name="nameSet">本次已抽中人</param>
/// <param name="nameTmp">當前抽中人</param>
/// <param name="times">已抽中人數</param>
/// 通過迴圈遞迴,將
///
/// <returns>不重複的獲獎人</returns>
public String getNewName(String[] nameSet, String nameTmp, int times)
{
int n = 0;
string finalName = "";
while (n < times)
{
if (nameSet[n].ToString().Trim() == nameTmp.ToString().Trim())
{
nameTmp = getName();
finalName = getNewName(nameSet, nameTmp, times);
}
else
{
finalName = nameTmp;
}
n++;
}
return finalName;
}
/// <summary>
/// 抽取獲獎人-getName-宋榮凱-2017年3月14日
/// </summary>
/// 通過timer的開關,獲取label中的名字
/// <returns>獲獎人姓名</returns>
public String getName()
{
string name;
timer1.Start();
Thread.Sleep(100);
System.Windows.Forms.Application.DoEvents();
timer1.Stop();
name = this.lblgetName.Text;
return name;
}
#endregion
//從dataSet中刪去已抽中的人
public void deletLuky(int reCount)
{
for (int rows = 0; rows <= reCount; rows++)
{
if (ds.Tables[0].Rows[rows][2].ToString().Trim() == "是")
{
ds.Tables[0].Rows[rows].Delete();
ds.AcceptChanges();
reCount--;
rows--;
}
}
}
private void timer1_Tick(object sender, EventArgs e)
{
if (i > ds.Tables[0].Rows.Count - 1)
{
i = 0;
}
if (ds.Tables[0].Rows[i][1].ToString() != null)
{
this.lblgetName.Text = ds.Tables[0].Rows[i][1].ToString();
}
else
{
i = i + 1;
this.lblgetName.Text = ds.Tables[0].Rows[i][1].ToString();
}
i++;
}
【總結】
其實最後成型的這個抽獎演算法挺簡單也用啥思想, 但是設計過程中思考的時候去重借鑑過選擇排序,借鑑過散列表去重等等方法。第一次感覺演算法這麼有意思。 還是自己實際用到了才能感覺到好處。 感覺非常不錯。