實驗一 控制檯應用程序——隨機數
信息學院 網絡工程3班 黎健成 201130720310
一. 實驗要求
這是一個實際的項目衍生出來的核心算法之一。防僞碼是我們現在經常在商品上看到的防僞手段之一,現在需要編寫一個防僞碼生成器,按照輸入參數生成防僞碼,並且把生成的時間及指定的防僞碼輸出。
1)防僞碼的組成
防僞碼由以下字符組成:0123456789ABCDEFGHJKLMNPQRSTUVWXYZ
(數字1和字母I相近、數字0和字母O相近,所以去掉字母I和字母O。全部字母大寫)
2)在命令行中輸入2個參數,分別是:
防僞碼長度,防僞碼個數
例如:在命令行中調用程序爲:學號.exe 10 10000
指的是防僞碼長度爲10,生成10000個防僞碼。
3)防僞碼的生成及注意事項
防僞碼的長度由命令行參數決定;
所生成的防僞碼不能重複(按照以上例子,生成了10000個防僞碼,這10000個防僞碼就肯定不能重複)。
二. 設計思路
1. 首先根據格輸入“exe 防僞碼長度 防僞碼個數”讀入防僞碼的個數,由於C#不支持在一行裏面讀入兩個數字,所以寫了函數來實現這一功能。
核心代碼:
public static void ReadKeys(ref int icount, ref int ilength)
{
try
{
string[] str =Console.ReadLine().Split(' ');//根據空格拆分讀入字符串
icount =Convert.ToInt32(str[0]);
if (str.Length > 1)//如果查分到兩個字符串,即同時輸入了兩個參數
{
ilength =Convert.ToInt32(str[1]);
}
else//繼續讀下一個參數
{
ilength =Convert.ToInt32(Console.ReadLine());
}
}
catch (Exception ex)//如果輸入有誤,拋出異常
{
throw newException(ex.Message);
}
}
2. 接着根據防僞碼的長度(icount)和個數(ilength)產生防僞碼。並將產生的防僞碼加入到哈希集(hashSet)中。將防僞碼插入哈希集中,如果沒有加入成功,即哈希集中沒有重複的防僞碼則總個數加1。循環地生成規定長度的防僞碼直到總個數達到規定的要求。
核心代碼:
public static voidPlayRandom(int icount, int ilength)
{
try
{
int i = 0;
HashSet<string> hashSet =new HashSet<string>();
Random ra = newRandom(DateTime.Now.Millisecond);
StringBuilder strBuilder = newStringBuilder();
while (i < ilength)//當沒有達到規定長度的時候繼續生成防僞碼
{
strBuilder.Remove(0,strBuilder.Length);
for (int j = 0; j <icount; j++)
{
strBuilder.Append(randomList[ra.Next(randomList.Length)]);
}//根據長度,產生防僞碼
if(hashSet.Add(strBuilder.ToString())) i++;//如果沒有重複,總數加1
}
}
catch (Exception ex)
{
throw newException(ex.Message);
}
}程序運行效果圖
1.第一組測試數據(10 10000),見圖1。
2.第二組測試數據(20 1000000),見圖2。
3.第三組測試數據(50 1000000),見圖3。
三. 實驗總結
作爲本學期的第一個作業,乍一看上去,還是挺困難的。但是,看看QQ羣上的同學們的討論,加上在網絡上搜索的算法,也給了我不少的提示。
原先以爲這是一個非常複雜的過程,但是實際編程的時候確發現還是比較容易實現功能的。但是耗費大量時間纔出結果的確令人感到不爽。爲此,甚至還動用到了多線程,但是發現結果卻還更慢了。於是只好放棄。
最後,在我請教了我班的大神之後,我改用了利用哈希表中的Contains方法直接比較兩個防僞碼的哈希碼,而不是用ContainsValue直接比較兩個防僞碼是否相同,這樣,由原先的O(n)時間複雜度降爲O(1),就可以發現防僞碼是否重複。
後來,在課上聽老師說了哈希集(HashSet)的使用方法,然後就將容器由哈希表改爲了哈希集,結果速度又有了很大的提升,更加使我感到無比地興奮!
總體來說,這次的實驗讓我對哈希表和哈希集的運用有所加深,也深刻體會到了其便捷。
附完整代碼:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
using System.Collections;
namespace test1
{
class Program
{
public static string randomList = "0123456789ABCDEFGHJKLMNPQRSTUVWXYZ";
public static void Pause()
{
Console.Write("請鍵入任意字符以結束……");
Console.ReadKey(true);
}
public static void ReadKeys(ref int icount, ref int ilength)
{
try
{
Console.WriteLine("輸入格式:長度 個數");
string[] str = Console.ReadLine().Split(' ');
icount = Convert.ToInt32(str[0]);
if (str.Length > 1)
{
ilength = Convert.ToInt32(str[1]);
}
else
{
ilength = Convert.ToInt32(Console.ReadLine());
}
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
}
public static void PlayRandom(int icount, int ilength)
{
try
{
int i = 0;
HashSet<string> hashSet = new HashSet<string>();
Random ra = new Random(DateTime.Now.Millisecond);
StringBuilder strBuilder = new StringBuilder();
while (i < ilength)
{
strBuilder.Remove(0, strBuilder.Length);
for (int j = 0; j < icount; j++)
{
strBuilder.Append(randomList[ra.Next(randomList.Length)]);
}
if (hashSet.Add(strBuilder.ToString())) i++;
}
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
}
static void Main(string[] args)
{
Stopwatch timer1 = new Stopwatch();
int icount = 0, ilength = 0;
try
{
ReadKeys(ref icount, ref ilength);//讀入防僞碼長度,個數
timer1.Start();
PlayRandom(icount, ilength);//產生隨機防僞碼
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
finally
{
timer1.Stop();
double dMilliseconds = timer1.Elapsed.TotalMilliseconds;
Console.WriteLine("生成個數爲:{0},運行時間爲:{1}ms", ilength, dMilliseconds);
}
Pause();
}
}
}