1. 程式人生 > >【專案實戰】從抽獎演算法感受演算法奧妙

【專案實戰】從抽獎演算法感受演算法奧妙

【前言】

           最近因為公司年會需要,組長讓我們花幾天時間開發了個抽獎系統。雖然就是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++;
          
        }

【總結】

      其實最後成型的這個抽獎演算法挺簡單也用啥思想, 但是設計過程中思考的時候去重借鑑過選擇排序,借鑑過散列表去重等等方法。第一次感覺演算法這麼有意思。 還是自己實際用到了才能感覺到好處。 感覺非常不錯。