我正在尝试解决可以找到的here
的SPOJ问题以下是我的解决方案:
package main
import "fmt"
import "bufio"
import "os"
func main() {
var n, k int
var num int
var divisible int
in := bufio.NewReader(os.Stdin)
fmt.Fscan(in, &n)
fmt.Fscan(in, &k)
for n > 0 {
fmt.Fscan(in, &num)
if num%k == 0 {
divisible++
}
n--
}
fmt.Println(divisible)
}
代码工作正常。这里的问题是我在SPOJ中执行它时会出现超时。
我第一次只使用fmt.Scan
,但后来遇到this线程,建议我使用bufio
代替更快的输入扫描。
但我仍然遇到超时问题。我只是循环以获取所有输入,并且在此循环内我确定输入是否可被整除。所以,我认为它不是循环,而是输入扫描需要时间。如何改进这一点以更快地读取输入?或者是其他地方的问题?
答案 0 :(得分:9)
您可以使用bufio.Scanner
从输入中读取行。
由于我们一直在阅读数字,因此我们可以创建一个高度优化的转换器来获取数字。我们应该避免使用创建string
的{{3}},因为我们可以从Scanner.Text()
返回的原始字节中获取数字。 Scanner.Text()
返回与Scanner.Bytes()
相同的标记,但它首先转换为string
,这显然较慢,并生成“垃圾”并为gc工作。
所以这是一个转换器函数,它从原始字节中获取int
:
func toInt(buf []byte) (n int) {
for _, v := range buf {
n = n*10 + int(v-'0')
}
return
}
此toInt()
有效,因为[]byte
包含数字十进制格式的字符串表示形式的UTF-8编码字节序列,其中仅包含'0'..'9'
范围内的数字其UTF-8编码字节一对一映射(一个字节用于一个数字)。从数字到字节的映射只是一个转变:'0' -> 48
,'1' -> 49
等。
使用此完整的应用程序:
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
var n, k, c int
scanner := bufio.NewScanner(os.Stdin)
scanner.Scan()
fmt.Sscanf(scanner.Text(), "%d %d", &n, &k)
for ;n > 0; n-- {
scanner.Scan()
if toInt(scanner.Bytes())%k == 0 {
c++
}
}
fmt.Println(c)
}
func toInt(buf []byte) (n int) {
for _, v := range buf {
n = n*10 + int(v-'0')
}
return
}
此解决方案比调用Scanner.Bytes()
的速度快4倍。
备注:强>
在上面的解决方案中,我假设输入是有效的,即它始终包含有效数字,并且在第一个之后包含至少n
行(这给出了n
和k
)。
如果输入在n+1
行之后关闭,我们可以使用简化的for
(我们甚至不需要递减并依赖n
):
for scanner.Scan() {
if toInt(scanner.Bytes())%k == 0 {
c++
}
}
答案 1 :(得分:1)
尝试使用bufio.Scanner
(如您提到的主题中所示):
fmt.Scan(&n)
fmt.Scan(&k)
scanner := bufio.NewScanner(os.Stdin)
for n > 0 {
scanner.Scan()
k, _ := strconv.Atoi(scanner.Text())
...
答案 2 :(得分:0)
我编写了3个版本进行比较。
第一个使用fmt.Scanf("%d", &v)
,第二个使用字节转换数字(例如@icza),第三个使用strconv.Atoi
进行转换。要以这种方式使用初始化扫描仪的功能:
scanner := bufio.NewScanner(os.Stdin)
scanner.Split(bufio.ScanWords)
因此,每次我调用scan.Scan时,它都会返回一个由空格分隔的令牌。 功能如下:
func scanFromBytes(scanner *bufio.Scanner) (n int) {
scanner.Scan()
buf := scanner.Bytes()
for _, v := range buf {
n = n*10 + int(v-'0')
}
return
}
并且:
func scanAtoi(scanner *bufio.Scanner) (n int) {
scanner.Scan()
n, _ = strconv.Atoi(scanner.Text())
return
}
我已经用一个大文件(40k个测试)进行了测试,每个测试读取大约8个整数。
fmt.Scanf
解决方案大约需要1.9s的时间(比其他解决方案要长)。
在这两个函数中,我得到了大约0.8s。但是scanAtoi
总是比scanFromBytes
少0.05秒,除了第一次(可能会发生一些缓存)。