首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >按字典顺序从范围中获取下一个数字(没有包含所有字符串的容器)

按字典顺序从范围中获取下一个数字(没有包含所有字符串的容器)
EN

Stack Overflow用户
提问于 2013-12-20 12:52:37
回答 3查看 1.3K关注 0票数 0

如何设计一个函数,在每个调用中返回指定数值范围内的下一个值--字符串representation...?的字典顺序

示例:范围8..203 -> 10,100.109,11,110.119,12,120.129,13,130.139,.,19,190.199,20,200.203,30.99。

Constraints:索引0..~INT_MAX,固定空间,O(范围长度)性能,最好是“懒散”,所以如果您停止中途迭代,您还没有浪费处理的精力。请不要发布蛮力“解决方案”的数字迭代,同时生成字符串,然后排序。

实用程序:如果您生成的数据最终需要按字典顺序显示或处理,那么词汇学系列将保证根据需要进行懒惰的生成,减少内存需求并消除一种排序。

背景:今天回答this question时,我的解决方案给出了数字顺序输出(即8、9、10、11、12),而不是如问题所示的字典顺序(10、11、12、8、8)。我以为这会很容易写出来或者找到一个解决方案,但是我的Google-foo让我失望了,它比我预期的要复杂,所以我想我会在这里收集/贡献.

(标记为C++是我的主要语言,我个人对C++解决方案特别感兴趣,但任何事情都是受欢迎的)

有人投票结束这个问题,是因为我对正在解决的问题没有表现出最低限度的理解(嗯!!;-P),或者是试图解决问题。,我的解决方案是作为一个答案,,因为我很高兴它被评论,并在堆叠溢出智慧的狂风中重现.O_o

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2013-12-20 13:08:32

这有点乱七八糟,所以我很想看看其他人是如何处理它的。在增量操作符中显式处理了这么多的边缘情况!

对于range lowhigh

  • 0后面是1
  • 短于high的数字后面总是有0附加版本(例如12->120)。
  • 以0-8结尾的high以外的数字后面跟着下一个整数。
  • low的数字和high一样多时,您可以在high (返回哨兵high + 1) 之后完成
    • 否则,在数字999...的结尾处,比high少一个数字。

  • 其他以9结尾的数字在尾随9s之前增加部分,但如果这导致尾随0,则将其移除,条件是该数字仍大于low

代码语言:javascript
运行
复制
template <typename T>
std::string str(const T& t)
{
    std::ostringstream oss; oss << t; return oss.str();
}

template <typename T>
class Lex_Counter
{
  public:
    typedef T value_type;

    Lex_Counter(T from, T to, T first = -1)
      : from_(from), to_(to),
        min_size_(str(from).size()), max_size_(str(to).size()),
        n_(first != -1 ? first : get_first()),
        max_unit_(pow(10, max_size_ - 1)), min_unit_(pow(10, min_size_ - 1))
    { }

    operator T() { return n_; }

    T& operator++()
    {
        if (n_ == 0)
            return n_ = 1;
        if (n_ < max_unit_ && n_ * 10 <= to_)
            return n_ = n_ * 10; // e.g. 10 -> 100, 89 -> 890
        if (n_ % 10 < 9 && n_ + 1 <= to_)
            return ++n_;         // e.g. 108 -> 109
        if (min_size_ == max_size_
            ? n_ == to_
            : (n_ == max_unit_ - 1 && to_ < 10 * max_unit_ - 10 || // 99/989
               n_ == to_ && to_ >= 10 * max_unit_ - 10))   // eg. 993
            return n_ = to_ + 1;

        // increment the right-most non-9 digit
        // note: all-9s case handled above (n_ == max_unit_ - 1 etc.)
        // e.g. 109 -> 11, 19 -> 2, 239999->24, 2999->3

        // comments below explain 230099 -> 230100

        // search from the right until we have exactly non-9 digit
        for (int k = 100; ; k *= 10)
            if (n_ % k != k - 1)
            {
                int l = k / 10; // n_ 230099, k 1000, l 100
                int r = ((n_ / l) + 1) * l; // 230100
                if (r > to_ && r / 10 < from_)
                    return n_ = from_; // e.g. from_ 8, r 20...
                while (r / 10 >= from_ && r % 10 == 0)
                    r /= 10; // e.g. 230100 -> 2301
                return n_ = r <= from_ ? from_ : r;
            }
        assert(false);
    }

  private:
    T get_first() const
    {
        if (min_size_ == max_size_ ||
            from_ / min_unit_ < 2 && from_ % min_unit_ == 0)
            return from_;

        // can "fall" from e.g. 321 to 1000
        return min_unit_ * 10;
    }
    T pow(T n, int exp)
        { return exp == 0 ? 1 : exp == 1 ? n : 10 * pow(n, exp - 1); }
    T from_, to_;
    size_t min_size_, max_size_;
    T n_;
    T max_unit_, min_unit_;
};

性能数字

我可以在-O2的标准英特尔机器/单线程MS编译器上,在1秒钟内计数0到10亿。

运行我在Shahbaz的解决方案--以下--的机器/线束需要超过3.5秒才能计算到100,000。也许std::set不是一个好的堆/堆替代品,或者有更好的方法来使用它?欢迎任何优化建议。

代码语言:javascript
运行
复制
template <typename T>
struct Shahbaz
{
    std::set<std::string> s;
    Shahbaz(T from, T to)
      : to_(to)
    {
        s.insert(str(from));
        for (int n = 10; n < to_; n *= 10)
            if (n > from) s.insert(str(n));
        n_ = atoi(s.begin()->c_str());
    }

    operator T() const { return n_; }

    Shahbaz& operator++()
    {
        if (s.empty())
            n_ = to_ + 1;
        else
        {
            s.erase(s.begin());
            if (n_ + 1 <= to_)
            {
                s.insert(str(n_ + 1));
                n_ = atoi(s.begin()->c_str());
            }
        }
        return *this;
    }

  private:
    T n_, to_;
};

Perf代码供参考。

代码语言:javascript
运行
复制
void perf()
{
    DWORD start = GetTickCount();
    int to = 1000 *1000;
    // Lex_Counter<int> counter(0, to);
    Shahbaz<int> counter(0, to);

    while (counter <= to)
        ++counter;
    DWORD elapsed = GetTickCount() - start;
    std::cout << '~' << elapsed << "ms\n";
}
票数 1
EN

Stack Overflow用户

发布于 2013-12-20 15:31:19

下面是我在Python中的尝试:

代码语言:javascript
运行
复制
import math

#iterates through all numbers between start and end, that start with `cur`'s digits
def lex(start, end, cur=0):
    if cur > end:
        return
    if cur >= start:
        yield cur
    for i in range(0,10):
        #add 0-9 to the right of the current number
        next_cur = cur * 10 + i
        if next_cur == 0: 
            #we already yielded 0, no need to do it again
            continue
        for ret in lex(start, end, next_cur):
            yield ret

print list(lex(8, 203))

结果:

代码语言:javascript
运行
复制
[10, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 11, 110, 111, 112, 113, 
114, 115, 116, 117, 118, 119, 12, 120, 121, 122, 123, 124, 125, 126, 127, 128, 
129, 13, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 14, 140, 141, 142, 
143, 144, 145, 146, 147, 148, 149, 15, 150, 151, 152, 153, 154, 155, 156, 157, 
158, 159, 16, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 17, 170, 171, 
172, 173, 174, 175, 176, 177, 178, 179, 18, 180, 181, 182, 183, 184, 185, 186, 
187, 188, 189, 19, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 20, 200, 
201, 202, 203, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 
37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 
57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 
77, 78, 79, 8, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 9, 90, 91, 92, 93, 94, 95, 
96, 97, 98, 99]

这使用O(log(end))堆栈空间,它是由INT_MAX限制的,因此它不会深入到对典型的16位int的五个调用。它在O(end)时间内运行,因为它必须遍历小于start的数字,才能开始生成有效的数字。如果startend很大且紧密相连,这可能比O(结束启动)糟糕得多。

在我的机器上迭代lex(0, 1000000)大约需要6秒,所以它看起来比Tony的方法慢,但比Shahbaz的要快。当然,直接比较是很困难的,因为我使用的是一种不同的语言。

票数 2
EN

Stack Overflow用户

发布于 2013-12-29 12:07:26

一些Java代码(由此派生的C++代码应该是微不足道的),非常类似于凯文的Python解决方案:

代码语言:javascript
运行
复制
public static void generateLexicographical(int lower, int upper)
{
   for (int i = 1; i < 10; i++)
      generateLexicographical(lower, upper, i);
}

private static void generateLexicographical(int lower, int upper, int current)
{
   if (lower <= current && current <= upper)
      System.out.println(current);

   if (current > upper)
      return;

   for (int i = 0; i < 10; i++)
      generateLexicographical(lower, upper, 10*current + i);
}

public static void main(String[] args)
{
   generateLexicographical(11, 1001);
}

if语句的顺序并不重要,一个语句可以成为另一个语句的else,但是以任何奇怪的方式对它们进行更改都会花费大约20%的时间。

这只是从每个数字从1到10开始,然后递归地将每个可能的数字从0到10附加到那个数字,直到我们得到一个大于上限的数字。

它同样使用O(log upper)空间(每一个数字都需要一个堆栈帧)和O(upper)时间(我们从1upper)。

在这里,I/O显然是最耗时的部分。如果将其删除并替换为增量变量,则generateLexicographical(0, 100_000_000);大约需要4秒时间,但绝不是从适当的基准测试中提取出来的。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/20704050

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档