Генерация следующего доступного уникального имени в C#

Мы можем объединить обе перегрузки GetRand как шаблон функции.

Прежде всего, обратите внимание, что эффект std::uniform_real_distribution не определен, если T не является одним из float, double и long double. Например, 29.6.1.1 Общие требования [rand.req.genl] в проекте стандарта C ++ n4687 гласит:

В этом подпункте 29,6 эффект создания шаблона:

...

d) который имеет параметр типа шаблона с именем RealType не определен, если только соответствующий аргумент шаблона не является cv-unqualified и не является одним из float , double , или длинный двойной .

blockquote>

Кроме того, 29,6 .8.2.2 Шаблон классаiform_real_distribution [rand.dist.uni.real] описывает std::uniform_real_distribution с параметром типа шаблона ] RealType и, следовательно, std::uniform_real_distribution не определено:

template
class uniform_real_distribution {
    ...
};
blockquote>

Также аналогичное ограничение существует для std::uniform_int_distribution [ 1122]. Таким образом, нам нужно переключить тип распределения между std::uniform_real_distribution и std::uniform_int_distribution в зависимости от T.


Мы можем проверить вышеуказанные ограничения, используя std::is_floating_point и std::is_integral и сделать следующее переключение:

#include 
#include 

template
using uniform_distribution = 
typename std::conditional<
    std::is_floating_point::value,
    std::uniform_real_distribution,
    typename std::conditional<
        std::is_integral::value,
        std::uniform_int_distribution,
        void
    >::type
>::type;

Тогда две перегрузки GetRand могут быть объединены в следующий шаблон функции , Здесь я также избегаю рекурсивного построения std::mt19937_64 и делаю функцию поточно-ориентированной, применяя принятый ответ в этого поста .

template 
T GetRand(T lower, T upper)
{
    static thread_local std::random_device rd;
    static thread_local std::mt19937_64 mt(rd());        
    uniform_distribution dist(lower,upper);

    return dist(mt);
}

Наконец, сторона вызывающего абонента будет выглядеть следующим образом:

DEMO

auto i = GetRand   (0, 1); // 0 or 1
auto f = GetRand (0, 1); // [0, 1)
auto d = GetRand(0, 1); // [0, 1)

5
задан Joan Venge 8 April 2009 в 19:41
поделиться

8 ответов

Я бы создал статическое целое число в классе действий, которое увеличивается и присваивается как часть каждого нового экземпляра класса. Например:

class Blur
{
    private static int count = 0;

    private string _name;
    public string Name
    {
        get { return _name; }
        set { _name = value; }
    }

    public Blur()
    {
        _name = "Blur" + count++.ToString();
    }
}

Так как count является статическим, каждый раз, когда вы создаете новый класс, он будет увеличиваться и добавляться к имени по умолчанию. O (1) раз.

РЕДАКТИРОВАТЬ

Если вам нужно заполнить отверстия при удалении, я бы предложил следующее. Он будет автоматически ставить в очередь номера при переименовании элементов, но в целом это будет стоить дороже:

class Blur
    {
        private static int count = 0;
        private static Queue<int> deletions = new Queue<int>();

        private string _name;
        public string Name
        {
            get { return _name; }
            set
            {
                _name = value;
                Delete();
            }
        }

        private int assigned;

        public Blur()
        {
            if (deletions.Count > 0)
            {
                assigned = deletions.Dequeue();
            }
            else
            {
                assigned = count++;
            }
            _name = "Blur" + assigned.ToString();
        }

        public void Delete()
        {
            if (assigned >= 0)
            {
                deletions.Enqueue(assigned);
                assigned = -1;
            }
        }
    }

Кроме того, при удалении объекта вам нужно будет вызывать .Delete () для объекта.

CounterClass Dictionary version

class CounterClass
{
   private int count;
   private Queue<int> deletions;

   public CounterClass()
   {
      count = 0;
      deletions = new Queue<int>();
   }

   public string GetNumber()
   {
      if (deletions.Count > 0)
      {
          return deletions.Dequeue().ToString();
      }
      return count++.ToString();
   }

   public void Delete(int num)
   {
      deletions.Enqueue(num);
   }
}

Вы можете создать словарь для поиска счетчиков для каждой строки. Просто убедитесь, что вы анализируете индекс и вызываете .Delete (int) всякий раз, когда переименовываете или удаляете значение.

7
ответ дан 18 December 2019 в 07:32
поделиться

Я отсылаю вас к двум правилам оптимизации программ Майкла А. Джексона :

  1. Дон не делайте этого.
  2. Только для экспертов: пока не делайте этого.

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

Я хотел бы начать с простого: создать имя кандидата (например, «Sharpen01»), а затем перебрать существующие фильтры, чтобы увидеть, существует ли это имя. Если это так, увеличьте значение и попробуйте снова. Это O (N 2 ), но пока вы не получите тысячи фильтров, этого будет достаточно.

Если через некоторое время O (N 2 ) станет проблема, тогда я бы начал с создания HashSet существующих имен. Затем вы можете проверить каждое имя кандидата по HashSet, а не итерировать. Восстанавливайте HashSet каждый раз, когда вам нужно уникальное имя, а затем выбрасывайте его; вам не нужна сложность поддержания его перед лицом изменений. Это сделало бы ваш код простым в обслуживании, хотя только O (N).

O (N) будет достаточно. Вам не нужно O (1). Пользователь не собирается нажимать кнопку «Резкость» достаточное количество раз, чтобы была какая-либо разница.

7
ответ дан 18 December 2019 в 07:32
поделиться

Вы можете легко сделать это в O (м) где м является числом существующих экземпляров имени (и не зависит от n , количества элементов в списке.

  1. Найдите строку S , о которой идет речь. Если ] S нет в списке, все готово.
  2. S существует, поэтому создайте S + "01" и проверьте это. Продолжайте увеличивать (например, следующая попытка S + «02» , пока его не существует.

Это дает вам уникальные имена, но они все еще «красивые» и удобочитаемые.

Если вы не ожидаете большого количества дубликатов, это должно быть "почти постоянным" временем, потому что м будет очень маленьким.

Предостережение: Что если строка естественно оканчивается на например "01 "? В вашем случае это звучит маловероятно, поэтому, возможно, вам все равно. Если вам все равно, рассмотрите возможность добавления дополнительного суффикса, например" _01 "вместо просто" 01 «так их легче отличить.

3
ответ дан 18 December 2019 в 07:32
поделиться

Вы можете сделать что-то вроде этого:

    private Dictionary<string, int> instanceCounts = new Dictionary<string, int>();

    private string GetNextName(string baseName)
    {
        int count = 1;

        if (instanceCounts.TryGetValue(baseName, out count))
        {
            // the thing already exists, so add one to it
            count++;
        }

        // update the dictionary with the new value
        instanceCounts[baseName] = count;

        // format the number as desired
        return baseName + count.ToString("00");
    }

Затем вы просто используете это, вызывая GetNextName (...) с базовым именем, которое вы хотели, например, string myNextName = GetNextName ("Blur");

Используя это, вам не нужно будет предварительно инициализировать словарь. Это будет заполнять, как вы использовали различные базовые слова. Также это O (1).

2
ответ дан 18 December 2019 в 07:32
поделиться

Если вы хотите O (1) время, просто отследите, сколько экземпляров каждого у вас есть. Сохраняйте хеш-таблицу со всеми возможными объектами, при создании объекта увеличивайте значение для этого объекта и используйте результат в имени.

0
ответ дан 18 December 2019 в 07:32
поделиться

Я бы искал способы упростить проблему.

Существуют ли какие-либо ограничения, которые могут быть применены? Например, было бы достаточно, если бы у каждого пользователя был только один (активный) тип действия? Затем действия можно различить по имени (или идентификатору) пользователя.

  • Blur (Бен Ф)
  • Blur (Адриан Х)
  • Фокус (Бен Ф)

Возможно, это не вариант в этом случае, но, возможно, что-то еще будет возможно. Я бы пошел на многое, чтобы избежать сложности некоторых из предложенных решений!

1
ответ дан 18 December 2019 в 07:32
поделиться

Я бы создал словарь со строковым ключом и целочисленным значением, сохраняя следующий номер для использования в заданном действии , На практике это будет почти O (1).

private IDictionary<String, Int32> NextFreeActionNumbers = null;       

private void InitializeNextFreeActionNumbers()
{
   this.NextFreeActionNumbers = new Dictionary<String, Int32>();

   this.NextFreeActionNumbers.Add("Blur", 1);
   this.NextFreeActionNumbers.Add("Sharpen", 1);
   this.NextFreeActionNumbers.Add("Contrast", 1);
   // ... and so on ...
}

private String GetNextActionName(String action)
{
   Int32 number = this.NextFreeActionNumbers[action];

   this.NextFreeActionNumbers[action] = number + 1;

   return String.Format("{0} {1}", action, number);
}

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

Из-за комментариев по повторному использованию отверстий я тоже хочу добавить его здесь. Не используйте повторно созданные дыры при переименовании или удалении.

1
ответ дан 18 December 2019 в 07:32
поделиться

Вы определенно не хотите показывать GUID для пользовательского интерфейса.

Вы предлагаете исходное имя, например «Blur04», позволяющее пользователю переименовывать его, а затем выдает сообщение об ошибке, если пользовательское имя конфликтует? Или переименовать его в «CustomName01» или как-то еще?

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

0
ответ дан 18 December 2019 в 07:32
поделиться
Другие вопросы по тегам:

Похожие вопросы: