【问题标题】:Workaround Go's map pointer issue解决方法 Go 的地图指针问题
【发布时间】:2021-10-06 10:31:47
【问题描述】:

我正在解决this Project Euler question。首先我尝试了蛮力,它花了 0.5 秒,然后我尝试了动态编程来利用记忆化,预计会有很大的改进,但我惊讶的是结果是 0.36 秒。

经过一番谷歌搜索后,我发现您不能在函数 (find_collat​​z_len) 中使用指向外部地图数据 (备忘录) 的指针。因此,每次运行下面的函数时,它都会复制整个字典。这听起来像是对处理器能力的巨大浪费。

我的问题是什么是解决方法,以便我可以使用指向函数外部映射的指针来避免复制。

这是我丑陋的代码:

package main
//project euler 014 - longest collatz sequence

import (
    "fmt"
    "time"
)

func find_collatz_len(n int, memo map[int]int) int {
    counter := 1
    initital_value := n
    for n != 1 {
        counter++
        if n < initital_value {
            counter = counter + memo[n]
            break
        }
        if n%2 == 0 {
            n = int(float64(n)/2)
        } else {
            n = n*3+1
        }
    }
    memo[initital_value] = counter
    return counter
}

func main() {
    start := time.Now()
    max_length := 0
    number := 0
    current_length := 0
    memo := make(map[int]int)
    for i:=1; i<1_000_000; i++ {
        current_length = find_collatz_len(i, memo)
        if current_length > max_length {
            max_length = current_length 
            number = i
        }
    }
    fmt.Println(max_length, number)
    fmt.Println("Time:", time.Since(start).Seconds())
}

【问题讨论】:

  • “所以每次运行下面的函数时,它都会复制整个字典。” -- 不,这不是正在发生的事情。
  • 不存在“映射指针问题”。如果你愿意,你可以使用指针,这里没有理由。
  • "item is not scriptable" -- 不是来自 Go 编译器或运行时的实际错误消息,因此很难确定您遇到的实际错误是什么.在不知道实际错误的情况下,我们无法提供如何修复它的建议。
  • 就优化而言,您可以使用make(map[int]int, 1_000_000) 将地图初始化为其最终大小,从而避免运行时动态增长地图。 (这将我机器上的时间缩短了近一半 [从 ~0.28 到 ~0.16])
  • 地图已经是底层的指针。传递一个映射值将传递一个指针。正如@mkopriva 所建议的,使用预期的最终容量初始化地图:make(map[int]int, 1_000_000)。这会将运行时间从 0.3 秒减少到 0.2 秒。您也可以将int(float64(n)/2) 替换为n/2,因为在您使用的整数范围内,它们会给出相同的结果。这将使您进一步提高 5%(在我的机器上为 0.19 秒)。

标签: algorithm dictionary go


【解决方案1】:

地图已经是引擎盖下的指针。传递一个映射值将传递一个指针。详情见why slice values can sometimes go stale but never map values?

当创建没有容量提示的映射时,映射的内部结构足以存储相对较少的条目(大约 7 个)。随着地图的增长,实现有时需要分配更多的内存并重组(重新散列)地图以容纳更多元素。如果您按照@mkopriva 的建议使用预期的最终容量初始化地图,则可以避免这种情况:

memo := make(map[int]int, 1_000_000).

因此,将分配足够的空间来存储所有条目(在您的示例中为1_000_000),因此在您的应用程序的生命周期内不会发生重新哈希。这会将运行时间从 0.3 秒减少到 0.2 秒。

您也可以将int(float64(n)/2) 替换为n/2,因为在您使用的整数范围内,它们会给出相同的结果。这将使您进一步提高 5%(在我的机器上为 0.19 秒)。

【讨论】:

  • 感谢您的全面回答。真的很喜欢。
猜你喜欢
  • 2022-01-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-12
  • 2016-06-17
  • 1970-01-01
  • 1970-01-01
  • 2022-10-04
相关资源
最近更新 更多