Archive

Archive for March 26th, 2010

Биржа, арбитраж и программирование. Часть 2. Арбитраж.

March 26th, 2010 No comments

Вторая часть «трилогии» об индексном арбитраже и применении программирования при занятии этой деятельностью. С первой частью вы можете ознакомиться здесь.

Сегодня, возвращаясь домой, я думал как же начать рассказывать об арбитраже. Тема эта интересна, увлекательна, но в тоже время рассчеты достаточно сложны. Рассказать об арбитраже не затрагивая рассчетов довольно сложно, и я не знал с какого края подступиться, поэтому начну свой рассказ с некоторых терминов, и немного издалека…

Подробнее

Categories: Uncategorized Tags:

Fun with recursion

March 26th, 2010 No comments
Categories: Uncategorized Tags:

DevConf::ASP.NET() – событие, которое не стоит пропускать

March 26th, 2010 No comments

Разработчики .NET и ASP.NET, скоро, а именно 17 мая 2010 года, в городе-герое Москва, пройдет очень крупная конференция DevConf. DevConf — профессиональная конференция, посвященная ведущим технологиям программирования и вебразработки. Уникальность DevConf в том, что она объединит в себе участников-разработчиков для большого числа платформ. В день проведения конференции планируется организовать потоки докладов: DevConf::PHPConf(), DevConf::Perl(), DevConf::Python(), DevConf::Ruby(), DevConf::ASP.NET(). Отдельно от этого, 18 мая, пройдет поток докладов посвященные RIA-технологиям: Flash, Silverlight и другим.
У вас как у разработчиков есть два варианта участия в конференции: как зритель и как доладчик. Смотреть на чужие доклады интересно, но я приглашаю всех вас присоединиться к докладчикам и подать заявки на собственные доклады на DevConf::ASP.NET(). На данный момент, зарегистрировано 4 заявки на доклады, в том числе и моя, в которой я расскажу про фреймворк MEF.
Участие в конференциях — это не только новые знания. Это новые знакомства, связи, общение. Приходите на DevConf::ASP.NET(). Участвуйте в DevConf::ASP.NET() как докладчик. Делитесь опытом или набирайтесь знаний.

Categories: Uncategorized Tags:

Учимся меморизировать

March 26th, 2010 No comments

euler_mainВ предыдущей загадке было предложено написать код, который вычислял бы длину последовательности. На самом деле, эта задачка была взята не из головы, а с замечательного сайта http://projecteuler.net. Он посвящен решению математических задач на различных языках программирования. У каждого пользователя на сайте есть свой рейтинг, как и у каждого языка программирования(кстати, большинство задач решается например на c/c++, а большинство «решающих задачи» из США).

Про этот сайт я узнал из статьи Билла Вагнера. В ней он предлагает свой способ решения задачи.

Для начала Билл предлагает решить задачу с помощью одной рекурсивной функции:

private static long Generate(long n)
{
  if (n == 1) { return 1; }
  var next = (n % 2 == 0) ? n / 2 : 3 * n + 1;
  return Generate(next) + 1;
}

и обычного LINQ-запроса:

var answer = (from StartingValue in Enumerable.Range(1, 999999)
let SequenceSize = Generate(StartingValue)
orderby SequenceSize descending
select new { StartingValue, SequenceSize }).First();

Это работает и дает правильный ответ, но Билл этим решением не слишком удовлетворен, т.к. этот процесс занимает примерно 15 секунд на его портативном коммуникаторе.

Как сделать это быстрей? Билл предлагает метод, названым Memoization. Мы же на русский манер будем его называть «Меморизацией». В основе этого метода лежит сохранение результата подсчетов и последующий возврат к сохраненному результату, а не повторный подсчет. Это может обеспечить выигрыш в нашей рекурентной функции «Generate». К примеру меморизация результата для числа 64 означает сохранение подсчетов для чисел 64, 32, 16, 8, 4, 2 и 1.

Можно изменить метод «Generate», чтобы обеспечить хранение для подсчитанных ранее результатов:

private static Dictionary<long, long> StoredResults = new Dictionary<long, long>();
 
private static long Generate(long n)
{
	if (StoredResults.ContainsKey(n))
		return StoredResults[n];
 
	if (n == 1)
	{
		StoredResults[n] = 1;
		return 1;
	}
 
	var next = (n % 2 == 0) ? n / 2 : 3 * n + 1;
	var answer = Generate(next) + 1;
	StoredResults[n] = answer;
	return answer;
}

Но в чем смысл? Здесь нет повторного использования ранее полученных значений.

Далее, Билл предлагает написать Generic функцию для меморизации, которая запоминала бы какую угодно функцию с одной переменной. Пост Вэса Дайера объясняет эту технологию в деталях.

Меморизация – это Generic метод, который абстрагируется как от типа параметра, так и от типа результата.

// Что такое Func?
// Это делегат из пространства System:
// public delegate TResult Func<T, TResult>(T arg);
 
// Добавляем метод Memoize классу Func<T, TResult>
// RTFM: Методы расширения (http://msdn.microsoft.com/ru-ru/library/bb383977.aspx)
public static Func<T, TResult> Memoize<T, TResult>(this Func<T, TResult> function)
{
	// Это кеш. Он статический.
	var previousResults = new Dictionary<T, TResult>();
 
	// Заменяем функцию function новой функцией, которая дергает
	// function() только тогда, когда результата для конкретного
	// аргумента еще нет в кеше.
	return arg =>
	{
		if (previousResults.ContainsKey(arg))
			return previousResults[arg];
		else
		{
			TResult result = function(arg);
			previousResults.Add(arg, result);
			return result;
		}
	};
}

Первый вопрос, который может у вас возникнуть: как на самом деле работают предыдущие результаты(previousResults). Это локальная переменная, а не статическая. Как она может существовать вне пределов метода?

Вот это и является волшебством «замыкания«. Memoize не возвращает значения, она возвращает некоторую функцию, которая позволяет вам найти это значение поздней.

Далее осталось объявить и определить функцию вычисления длины последовательности и заменить ее на наш «кеширующий» метод:

static void Main()
{
	// Обявляем функцию вычисления длины последовательнсоти.
	Func<long, long> GenerateSequenceSize = null;
 
	// Определяем ее (она рекурсинвая и трудоемкая).
	GenerateSequenceSize = (long n) =>
	{
		if (n == 1)
		{
			return 1;
		}
		var next = (n % 2 == 0) ? n / 2 : 3 * n + 1;
		return GenerateSequenceSize(next) + 1;
	};
 
	// Заменяем эту функцию ее "кеширующим" заместителем.
	GenerateSequenceSize = GenerateSequenceSize.Memoize();
 
	// Перебор чисел с использованием чудо-функции и LINQ.
	var answer = (from StartingValue in Enumerable.Range(1, 999999)
	              let SequenceSize = GenerateSequenceSize(StartingValue)
	              orderby SequenceSize descending
	              select new { StartingValue, SequenceSize }).First();
 
	// Вывод результата, занавес.
	Console.WriteLine(answer);
	Console.ReadLine();
}

Надеюсь, вам этот пост поможет сократить время выполнения многих ваших программ.

P.S. Спасибо Ляпину Дмитрию за комментирование кода.