C #
Hầu như hoàn toàn ngẫu nhiên và giải pháp lắp ráp thô. Theo như C # và khá nhiều nền tảng khác, đây là mức độ thấp nhất có thể. May mắn thay, C # cho phép bạn xác định các phương thức trong thời gian chạy trong IL (IL là ngôn ngữ trung gian, mã byte của .NET, tương tự như lắp ráp). Hạn chế duy nhất của mã này là tôi đã chọn một số opcode (trong số hàng trăm) với phân phối tùy ý sẽ cần thiết cho giải pháp hoàn hảo. Nếu chúng tôi cho phép tất cả các mã, cơ hội của một chương trình hoạt động là không có gì, vì vậy điều này là cần thiết (như bạn có thể tưởng tượng, có nhiều cách mà các hướng dẫn lắp ráp ngẫu nhiên có thể bị sập, nhưng may mắn thay, chúng không làm hỏng toàn bộ chương trình trong lưới). Khác với phạm vi của các opcodes có thể, nó cắt hoàn toàn ngẫu nhiên và cắt các opcode IL mà không có bất kỳ gợi ý nào.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Reflection.Emit;
using System.Diagnostics;
using System.Threading;
namespace codegolf
{
class Program
{
// decompile this into IL to find out the opcodes needed for the perfect algo
static int digitsumbest(int i)
{
var ret = 0;
while (i > 0)
{
ret += i % 10;
i /= 10;
}
return ret;
}
delegate int digitsumdelegate(int num);
static Thread bgthread;
// actually runs the generated code for one index
// it is invoked in a background thread, which we save so that it can be aborted in case of an infinite loop
static int run(digitsumdelegate del, int num)
{
bgthread = Thread.CurrentThread;
try
{
return del(num);
}
catch (ThreadAbortException)
{
bgthread = null;
throw;
}
}
// evaluates a generated code for some inputs and calculates an error level
// also supports a full run with logging
static long evaluate(digitsumdelegate del, TextWriter sw)
{
var error = 0L;
List<int> numbers;
if (sw == null) // quick evaluation
numbers = Enumerable.Range(1, 30).Concat(Enumerable.Range(1, 70).Select(x => 5000 + x * 31)).ToList();
else // full run
numbers = Enumerable.Range(1, 9999).ToList();
foreach (var num in numbers)
{
try
{
Func<digitsumdelegate, int, int> f = run;
bgthread = null;
var iar = f.BeginInvoke(del, num, null, null);
if (!iar.AsyncWaitHandle.WaitOne(10))
{
bgthread.Abort();
while (bgthread != null) ;
throw new Exception("timeout");
}
var result = f.EndInvoke(iar);
if (sw != null)
sw.WriteLine("{0};{1};{2};", num, digitsumbest(num), result);
var diff = result == 0 ? 15 : (result - digitsumbest(num));
if (diff > 50 || diff < -50)
diff = 50;
error += diff * diff;
}
catch (InvalidProgramException)
{
// invalid IL code, happens a lot, so let's make a shortcut
if (sw != null)
sw.WriteLine("invalid program");
return numbers.Count * (50 * 50) + 1;
}
catch (Exception ex)
{
if (sw != null)
sw.WriteLine("{0};{1};;{2}", num, digitsumbest(num), ex.Message);
error += 50 * 50;
}
}
return error;
}
// generates code from the given byte array
static digitsumdelegate emit(byte[] ops)
{
var dm = new DynamicMethod("w", typeof(int), new[] { typeof(int) });
var ilg = dm.GetILGenerator();
var loc = ilg.DeclareLocal(typeof(int));
// to support jumping anywhere, we will assign a label to every single opcode
var labels = Enumerable.Range(0, ops.Length).Select(x => ilg.DefineLabel()).ToArray();
for (var i = 0; i < ops.Length; i++)
{
ilg.MarkLabel(labels[i]);
// 3 types of jumps with 23 distribution each, 11 types of other opcodes with 17 distribution each = all 256 possibilities
// the opcodes were chosen based on the hand-coded working solution
var c = ops[i];
if (c < 23)
ilg.Emit(OpCodes.Br_S, labels[(i + 1 + c) % labels.Length]);
else if (c < 46)
ilg.Emit(OpCodes.Bgt_S, labels[(i + 1 + c - 23) % labels.Length]);
else if (c < 69)
ilg.Emit(OpCodes.Bge_S, labels[(i + 1 + c - 46) % labels.Length]);
else if (c < 86)
ilg.Emit(OpCodes.Ldc_I4, c - 70); // stack: +1
else if (c < 103)
ilg.Emit(OpCodes.Dup); // stack: +1
else if (c < 120)
ilg.Emit(OpCodes.Ldarg_0); // stack: +1
else if (c < 137)
ilg.Emit(OpCodes.Starg_S, 0); // stack: -1
else if (c < 154)
ilg.Emit(OpCodes.Ldloc, loc); // stack: +1
else if (c < 171)
ilg.Emit(OpCodes.Stloc, loc); // stack: -1
else if (c < 188)
ilg.Emit(OpCodes.Mul); // stack: -1
else if (c < 205)
ilg.Emit(OpCodes.Div); // stack: -1
else if (c < 222)
ilg.Emit(OpCodes.Rem); // stack: -1
else if (c < 239)
ilg.Emit(OpCodes.Add); // stack: -1
else
ilg.Emit(OpCodes.Sub); // stack: -1
}
ilg.Emit(OpCodes.Ret);
return (digitsumdelegate)dm.CreateDelegate(typeof(digitsumdelegate));
}
static void Main(string[] args)
{
System.Diagnostics.Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.Idle;
var rnd = new Random();
// the first list is just 10 small random ones
var best = new List<byte[]>();
for (var i = 0; i < 10; i++)
{
var initial = new byte[5];
for (var j = 0; j < initial.Length; j++)
initial[j] = (byte)rnd.Next(256);
best.Add(initial);
}
// load the best result from the previous run, if it exists
if (File.Exists("best.txt"))
best[0] = File.ReadAllLines("best.txt").Select(x => byte.Parse(x)).ToArray();
var stop = false;
// handle nice stopping with ctrl-c
Console.CancelKeyPress += (s, e) =>
{
stop = true;
e.Cancel = true;
};
while (!stop)
{
var candidates = new List<byte[]>();
// leave the 10 best arrays, plus generate 9 consecutive mutations for each of them = 100 candidates
for (var i = 0; i < 10; i++)
{
var s = best[i];
candidates.Add(s);
for (var j = 0; j < 9; j++)
{
// the optimal solution is about 20 opcodes, we keep the program length between 15 and 40
switch (rnd.Next(s.Length >= 40 ? 2 : 0, s.Length <= 15 ? 3 : 5))
{
case 0: // insert
case 1:
var c = new byte[s.Length + 1];
var idx = rnd.Next(0, s.Length);
Array.Copy(s, 0, c, 0, idx);
c[idx] = (byte)rnd.Next(256);
Array.Copy(s, idx, c, idx + 1, s.Length - idx);
candidates.Add(c);
s = c;
break;
case 2: // change
c = (byte[])s.Clone();
idx = rnd.Next(0, s.Length);
c[idx] = (byte)rnd.Next(256);
candidates.Add(c);
s = c;
break;
case 3: // remove
case 4: // remove
c = new byte[s.Length - 1];
idx = rnd.Next(0, s.Length);
Array.Copy(s, 0, c, 0, idx);
Array.Copy(s, idx + 1, c, idx, s.Length - idx - 1);
candidates.Add(c);
s = c;
break;
}
}
}
// score the candidates and select the best 10
var scores = Enumerable.Range(0, 100).ToDictionary(i => i, i => evaluate(emit(candidates[i]), null));
var bestidxes = scores.OrderBy(x => x.Value).Take(10).Select(x => x.Key).ToList();
Console.WriteLine("best score so far: {0}", scores[bestidxes[0]]);
best = bestidxes.Select(i => candidates[i]).ToList();
}
// output the code of the best solution
using (var sw = new StreamWriter("best.txt"))
{
foreach (var b in best[0])
sw.WriteLine(b);
}
// create a CSV file with the best solution
using (var sw = new StreamWriter("best.csv"))
{
sw.WriteLine("index;actual;generated;error");
evaluate(emit(best[0]), sw);
}
}
}
}
Xin lỗi tôi không có kết quả cho đến nay vì ngay cả khi thử nghiệm cho 1..99 (thay vì 1..9999) là khá chậm và tôi quá mệt mỏi. Sẽ lấy lại cho bạn vào ngày mai.
EDIT: Tôi đã hoàn thành chương trình và tinh chỉnh nó rất nhiều. Bây giờ, nếu bạn nhấn CTRL-C, nó sẽ kết thúc quá trình chạy hiện tại và xuất kết quả trong các tệp. Hiện tại, các giải pháp khả thi duy nhất mà nó tạo ra là các chương trình luôn trả về một số không đổi. Tôi bắt đầu nghĩ rằng cơ hội của một chương trình làm việc tiên tiến hơn là rất nhỏ. Dù sao tôi sẽ giữ cho nó chạy một thời gian.
EDIT: Tôi tiếp tục điều chỉnh thuật toán, nó là một món đồ chơi hoàn hảo cho một người đam mê như tôi. Tôi đã từng thấy một chương trình được tạo ra thực sự đã làm một số phép toán ngẫu nhiên và không phải lúc nào cũng trả về một số không đổi. Thật tuyệt vời khi chạy nó trên một vài triệu CPU cùng một lúc :). Sẽ tiếp tục chạy nó.
EDIT: Đây là kết quả của một số toán học hoàn toàn ngẫu nhiên. Nó nhảy xung quanh và ở mức 17 trong phần còn lại của các chỉ số. Nó sẽ không trở nên ý thức bất cứ lúc nào sớm.
EDIT: Nó đang trở nên phức tạp hơn. Tất nhiên, như bạn mong đợi, nó trông không giống thuật toán chữ số thích hợp, nhưng nó đang cố gắng hết sức. Hãy nhìn xem, một chương trình lắp ráp máy tính tạo ra!
no libraries
được phép có nghĩa là không có libc?