stackprobe7s_memo

何処にも披露する見込みの無いものを書き落とす場所

ナップサック問題(C#)

入力

public class Item // アイテム
{
	public int Value;  // このアイテムの価値 : 0 ~ 1,000,000 の範囲でランダムに生成
	public int Weight; // このアイテムの重さ : 0 ~     1,000 の範囲でランダムに生成
}

public class Condition // 与えられた条件
{
	public Item[] Items;       // 選択可能なアイテムリスト : 個数 0 ~ 1,000 の範囲でランダムに生成
	public int WeightCapacity; // 最大総重量               :      0 ~ 1,000 の範囲でランダムに生成
}

Solve0001

総当りで解く

public static Item[] Solve0001(Condition cond)
{
	Item[] best = null;
	bool[] selecteds = new bool[cond.Items.Length];
	bool forward = true;
	int index = 0;

	for (; ; )
	{
		if (forward)
		{
			if (cond.Items.Length <= index)
			{
				Item[] selectedItems = Enumerable.Range(0, cond.Items.Length).Where(i => selecteds[i]).Select(i => cond.Items[i]).ToArray();

				if (selectedItems.Select(item => item.Weight).Sum() <= cond.WeightCapacity)
					if (best == null || best.Select(item => item.Value).Sum() < selectedItems.Select(item => item.Value).Sum())
						best = selectedItems;

				forward = false;
			}
			else
			{
				selecteds[index] = true;
			}
		}
		else
		{
			if (selecteds[index])
			{
				selecteds[index] = false;
				forward = true;
			}
		}

		if (forward)
		{
			index++;
		}
		else
		{
			if (index <= 0)
				break;

			index--;
		}
	}
	return best;
}

Solve0002

動的計画法?で解く

private class SelectedItem
{
	public int ItemIndex;
	public int TotalValue;
	public SelectedItem Prev;
}

public static Item[] Solve0002(Condition cond)
{
	SelectedItem[,] table = new SelectedItem[cond.Items.Length, cond.WeightCapacity + 1];
	SelectedItem best = null;
	SelectedItem seli;

	for (int i = 0; i < cond.Items.Length; i++)
	{
		if (cond.Items[i].Weight <= cond.WeightCapacity)
		{
			table[i, cond.Items[i].Weight] = seli = new SelectedItem()
			{
				ItemIndex = i,
				TotalValue = cond.Items[i].Value,
				Prev = null,
			};

			if (best == null || best.TotalValue < seli.TotalValue)
				best = seli;
		}
	}
	for (int i = 0; i < cond.Items.Length; i++)
	{
		for (int w = 0; w <= cond.WeightCapacity; w++)
		{
			if (table[i, w] != null)
			{
				for (int ni = i + 1; ni < cond.Items.Length; ni++)
				{
					int nw = w + cond.Items[ni].Weight;

					if (nw <= cond.WeightCapacity)
					{
						int nv = table[i, w].TotalValue + cond.Items[ni].Value;

						if (table[ni, nw] == null || table[ni, nw].TotalValue < nv)
						{
							table[ni, nw] = seli = new SelectedItem()
							{
								ItemIndex = ni,
								TotalValue = nv,
								Prev = table[i, w],
							};

							if (best == null || best.TotalValue < seli.TotalValue)
								best = seli;
						}
					}
				}
			}
		}
	}
	List<Item> ret = new List<Item>();

	while (best != null)
	{
		ret.Add(cond.Items[best.ItemIndex]);
		best = best.Prev;
	}
	return ret.ToArray();
}

Extra

テスト用コード

public const int ITEM_VALUE_MIN = 0;
public const int ITEM_VALUE_MAX = 1000000;

public const int ITEM_WEIGHT_MIN = 0;
public const int ITEM_WEIGHT_MAX = 1000;

public const int ITEM_COUNT_MIN = 0;
//public const int ITEM_COUNT_MAX = 1000;

public const int WEIGHT_CAPACITY_MIN = 0;
public const int WEIGHT_CAPACITY_MAX = 1000;

public class Item
{
	public int Value;
	public int Weight;
}

public class Condition
{
	public Item[] Items;
	public int WeightCapacity;
}

public static Condition MakeCondition(int itemCountMax)
{
	Condition cond = new Condition();

	cond.Items = new Item[SecurityTools.CRandom.GetRange(ITEM_COUNT_MIN, itemCountMax)];
	cond.WeightCapacity = SecurityTools.CRandom.GetRange(WEIGHT_CAPACITY_MIN, WEIGHT_CAPACITY_MAX);

	for (int index = 0; index < cond.Items.Length; index++)
	{
		cond.Items[index] = new Item()
		{
			Value = SecurityTools.CRandom.GetRange(ITEM_VALUE_MIN, ITEM_VALUE_MAX),
			Weight = SecurityTools.CRandom.GetRange(ITEM_WEIGHT_MIN, ITEM_WEIGHT_MAX),
		};
	}
	return cond;
}

/// <summary>
/// 動作テスト
/// </summary>
public static void Test01()
{
	int testCnt = 0;

	while (Console.KeyAvailable == false)
	{
		Console.WriteLine("testCnt: " + (++testCnt));

		Condition cond = MakeCondition(17);
		//Condition cond = MakeCondition(18);
		//Condition cond = MakeCondition(19);
		//Condition cond = MakeCondition(20);

		Item[] ret1 = Solve0001(cond);
		Item[] ret2 = Solve0002(cond);

#if false
		// 最善の組み合わせが複数ある場合、異なる組み合わせを選ぶ可能性がある。

		if (ret1.Length != ret2.Length)
			throw null; // different !!!

#if false
		// 並びが一致するとは限らない。

		if (Enumerable.Range(0, ret1.Length).Any(index => ret1[index] != ret2[index]))
			throw null; // different !!!
#else
		if (ret1.Any(item => ret2.Contains(item) == false))
			throw null; // different !!!
#endif
#else
		if (ret1.Select(item => item.Value).Sum() != ret2.Select(item => item.Value).Sum())
			throw null; // different !!!
#endif
	}
}

/// <summary>
/// 速度テスト
/// </summary>
public static void Test02()
{
	int testCnt = 0;

	while (Console.KeyAvailable == false)
	{
		Console.WriteLine("testCnt: " + (++testCnt));

		Condition cond = MakeCondition(1000);

		DateTime stTm = DateTime.Now;
		Solve0002(cond);
		DateTime edTm = DateTime.Now;

		Console.WriteLine("実行時間 = " + (edTm - stTm).TotalMilliseconds.ToString("F3") + " ミリ秒");
	}
}

// Solve0001() 省略
// Solve0002() 省略