Member 11169882 Ответов: 6

Код C# слишком неэффективен, вызывая тайм-аут на hackerrank


Эй Ребята,

У меня было немного свободного времени, поэтому я выполнял задачу кодирования от HackerRank, и на 8-й день этой задачи, как бы я ни старался, я не мог заставить код работать более эффективно, так как некоторые тестовые случаи всегда "тайм-аут".

Если вы, ребята, могли бы уделить 2 минуты, глядя на мой код и видя, где он неэффективен, пожалуйста, дайте мне знать :)

В основном, в первом цикле for я читаю в парах ключ-значение, во втором цикле for я читаю в потенциальных ключах, если они имеют значение, то выводите ключ=значение, иначе выводите "не найдено". переменная "n"говорит мне, сколько пар значений ключей я читаю и сколько потенциальных ключей я читаю.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Collections;

class Solution {
    static void Main(String[] args) {
        /* Enter your code here. Read input from STDIN. Print output to STDOUT. Your class should be named Solution */
        int n = int.Parse(Console.ReadLine());
        string Word1 = "";
        Dictionary<string, int> list = new Dictionary<string, int>();
        
        for(int i = 0; i <n; i++)
        {
            Word1 = Console.ReadLine();
            string[] array = Word1.Split(null);
            list.Add(array[0], int.Parse(array[1]));
        }
        for(int i = 0; i<n; i++)
        {
            Word1 = Console.ReadLine();
            if(list.ContainsKey(Word1))
            {
                var keyValuePair = list.Single(x => x.Key == Word1);
                Console.WriteLine(Word1 + "=" + keyValuePair.Value);
            }
            else
            {
                Console.WriteLine("Not found");
            }
        }
        
        
    }
}


Что я уже пробовал:

Это решение определенно более эффективно, чем мое первое, где я использовал циклы foreach... но я не знаю ничего в этом случае, что было бы быстрее, чем LINQ

NotPolitcallyCorrect

LINQ != быстрее

Patrice T

"проблема кодирования от HackerRank" не очевидна для всех, так как не все зарегистрированы на этом сайте.
Можете ли вы сказать нам, в чем заключается это требование?

Member 11169882

В принципе, вы должны написать код, основанный на определенных входах и ожидаемых выходах. Проблема в том, что некоторые тестовые случаи огромны со 100 входными данными. Поэтому, хотя некоторые тестовые случаи проходят, не все могут, потому что в больших тестовых случаях происходит тайм-аут

Patrice T

Можете ли вы скопировать точное требование?
Я уверен, что детали имеют значение.

6 Ответов

Рейтинг:
46

Peter Vegter

Вы ищете "Word1" неэффективным способом, попробуйте это ("стандартный способ"):

for(int i = 0; i < n; i++)
{
    int val;

    Word1 = Console.ReadLine();
    if (list.TryGetValue(Word1, out val))
    {
        Console.WriteLine(Word1 + " = " + val);
    }
    else
    {
        Console.WriteLine(Word1 + " not found");
    }
}
или короче:
for(int i = 0; i < n; i++)
{
    int val;

    Word1 = Console.ReadLine();
    Console.WriteLine(Word1 + (list.TryGetValue(Word1, out val) ? " = " + val : " not found"));
}


Member 11169882

Спасибо!! TryGetValue определенно более эффективен.
Поскольку я должен выводить данные определенным образом, чтобы пройти тестовые случаи, это в конечном итоге сработало для меня:

for (int i = 0; i < n; i++)
{
int val;
Word1 = Консоль.Линия чтения();
если (список.TryGetValue(Слово1, выход вал))
{
Приставка.WriteLine(Word1 + " = " + val);
}
ещё
{
Приставка.WriteLine("не найден");
}
}

Member 11169882

Я считаю, что поиск будет работать лучше так, как я это сделал, если бы я не был внутри цикла, однако, поскольку я зацикливаюсь на каждом элементе, tryget гораздо эффективнее. Проблема заключалась в том, что раньше я использовал keyValuePair, а не словарь, и компилятор не принимал "TryGetValue", но теперь он работал, когда я сделал несколько настроек, используя ваше решение.
Еще раз спасибо!

Peter Vegter

Очень мило! Спасибо за ваш ответ.
С уважением, Питер

Рейтинг:
22

OriginalGriff

Как говорит NotPoliticallyCorrect, Linq не является автоматически быстрее - он может казаться быстрее, потому что обычно откладывает выполнение до тех пор, пока не потребуются результаты, но это вовсе не обязательно быстрее.

Вместо того, чтобы пытаться заставить нас улучшить его, начните делать базовые оптимизации: используйте класс Stopwatch для мониторинга вашего кода и выясните, где он проводит больше всего времени. Затем вы можете работать над улучшением этого конкретного сегмента кода.
И вам нужно знать, где он истекает и через какое время: возможно, набор данных, с которым вы тестируете, заставляет его замереть в ожидании ввода, который никогда не приходит, и именно поэтому он истекает. Если, например, во втором цикле есть n-1 строк, он будет ждать последней строки, пока она не будет убита.


Member 11169882

Я не обязательно просил кого-то улучшить или переписать код, я только спросил в этом случае, где может быть неэффективность в том, как я его написал, но я понимаю, что вы имеете в виду. Я ценю время общины и поэтому не прошу многого.

Кроме того, для второго цикла for я попытался использовать цикл while следующим образом
while ((Word1 = консоль.ReadLine ())!= null)
{
.....
}
Я все еще получаю тот же результат (происходит тайм-аут). А пока я перешел к следующему испытанию. Если я не получу никаких других решений в течение следующего дня, я приму ваше решение

CDP1802

Просто догадываюсь, но мой главный подозреваемый-это эта линия:

список.ContainsKey(Word1)

Если список большой и алгоритм поиска не подходит, поиск может занять много ненужного времени. Сортировка списка после прочтения может немного помочь. Попробуйте использовать как восходящий, так и нисходящий порядок.

Member 11169882

см. решение 4, Спасибо за ваш ответ!

Рейтинг:
2

Patrice T

Когда дело доходит до оптимизации скорости, инструментом выбора является профилировщик.
то профилировщик есть ли там, чтобы сказать вам, сколько времени вы тратите в каждой части вашего кода и обнаружить "узкие места".
Профилирование (компьютерное программирование) - Википедия[^]


Рейтинг:
2

Philippe Mori

Что-то подобное

list.Single(x => x.Key == Word1);
это неэффективно, потому что Linq-to-object не может понять, что он ищет словарь с его ключом. Таким образом, он будет по существу перечислять все элементы в словаре, пока не найдет совпадение.

С другой стороны, TryGetValue специально написан для эффективного поиска по словарю.

Для таких операций контейнерные методы были бы быстрее.

Linq-to-Entities или Linq-to-SQL различаются тем, что SQL-код генерируется путем анализа самого выражения.

Кроме того, использование `Single " подразумевает, что вы проверите все элементы, чтобы убедиться, что один из них существует. В среднем использование `первого " будет в два раза быстрее, и поскольку словарь все равно не может содержать дубликатов ключей, он даст тот же результат.


Member 11169882

Да, я понял это после того, как увидел решение 4. И спасибо Вам за ваше полезное понимание, такие советы действительно помогают мне стать лучшим разработчиком. В то время я пробовал разные вещи, и trygetvalue по какой-то причине не нравился компилятору (когда я пробовал его), я, вероятно, использовал его неправильно. Я все еще не самый лучший в использовании linq, но он очень полезен в .NET из-за моего использования его до сих пор. Еще раз ваш вклад очень ценится, хорошего вам дня!

Рейтинг:
1

#realJSOP

Почему бы вам не получить список слов от пользователя, сохранить их в списке, а затем выполнить свои циклы, используя слова из списка.


Member 11169882

См. решение 4, Спасибо за ваш ответ, очень ценю ваше время!

Рейтинг:
1

AFell2

Может быть, более чистая реализация LINQ, чтобы уйти от циклов for и перейти к итерациям?

Избегайте словаря & lt;string, int> boxing и храните массив LINQ как коллекцию анонимных объектов.

Кроме того, используйте SingeOrDefault и проверьте, является ли значение null. Вы можете объединить это в своей операции строки сообщения и (надеюсь) поместить все это в один и тот же цикл.

Дайте мне знать, если это вообще повысит эффективность:

class Solution
{
	static void Main(String[] args)
	{
		/* Enter your code here. Read input from STDIN. Print output to STDOUT. Your class should be named Solution */
		int n = int.Parse(Console.ReadLine());
		int[] Range = Enumerable.Range(0, n).ToArray();
		var list = Range.Select(e =>
		{
			string[] array = Console.ReadLine().Split(null);
			return new { key = array[0], val = int.Parse(array[1]) };
		});

		foreach (var r in Range)
		{
			var found = list.SingleOrDefault(l => l.key == Console.ReadLine());
			string msg = found == null ? "Not Found" : $"{found.key}={found.val}";
			Console.WriteLine(msg);
		}
	}
}


Member 11169882

Хотя ваше решение не сработало для меня, я хочу поблагодарить вас за ваше время и помощь :) Я тоже кое-чему научился из вашей реализации! См. решение 4 для того, что сработало для меня.
Спасибо