昨天我进行了一次有趣的采访,面试官问我一个经典问题:如何在不使用*
运算符的情况下将Java中的两个数字相乘。老实说,我不知道这是否是采访带来的压力,但我无法提出任何解决方案。
面试结束后,我回到家,轻松地通过SO寻求答案。到目前为止,我发现了以下内容:
第一种方法:使用For循环
// Using For loop
public static int multiplierLoop(int a, int b) {
int resultat = 0;
for (int i = 0; i < a; i++) {
resultat += b;
}
return resultat;
}
第二种方法:使用递归
// using Recursion
public static int multiplier(int a, int b) {
if ((a == 0) || (b == 0))
return 0;
else
return (a + multiplier(a, b - 1));
}
第三种方法:使用Log10
**// Using Math.Log10
public static double multiplierLog(int a, int b) {
return Math.pow(10, (Math.log10(a) + Math.log10(b)));
}**
所以现在我有两个问题要问你:
答案 0 :(得分:25)
我不知道这是否必须是一个严格的编程问题&#34;。但在数学中:
x * y = x / (1 / y) #divide by inverse
所以:
方法1 :
public static double multiplier(double a, double b) {
// return a / (1 / b);
// the above may be too rough
// Java doesn't know that "(a / (b / 0)) == 0"
// a special case for zero should probably be added:
return 0 == b ? 0 : a / (1 / b);
}
方法2 (更多&#34;编程/ API&#34;解决方案):
使用大小数,大整数:
new BigDecimal("3").multiply(new BigDecimal("9"))
可能还有其他一些方法。
答案 1 :(得分:14)
有一种名为Russian Peasant Multiplication的方法。在班次操作员的帮助下证明这一点,
public static int multiply(int n, int m)
{
int ans = 0, count = 0;
while (m > 0)
{
if (m % 2 == 1)
ans += n << count;
count++;
m /= 2;
}
return ans;
}
想法是将第一个数字加倍并重复减半第二个数字,直到第二个数字不变为1.在此过程中,每当第二个数字变为奇数时,我们将第一个数字加到结果中(结果初始化为0)另一个实现是,
static int russianPeasant(int n, int m) {
int ans = 0;
while (m > 0) {
if ((m & 1) != 0)
ans = ans + n;
n = n << 1;
m = m >> 1;
}
return ans;
}
参考:
答案 2 :(得分:11)
其他人已经在问题1上充分说明了我不会在这里重新讨论它,但我确实想在问题2上稍微打一针,因为看起来(对我来说)更有趣的一个。
所以,当有人问你这类问题时,他们不太关心你的代码是什么样的,而是更关心你的思考方式。在现实世界中,你实际上不必在没有*运算符的情况下编写乘法;人类已知的每种编程语言(我猜Brainfuck除外)都实现了乘法,几乎总是使用*运算符。关键是,有时您正在使用代码,并且出于任何原因(可能由于库膨胀,由于配置错误,由于包不兼容等),您将无法使用您使用的库至。我们的想法是看看你在这些情况下的运作方式。
问题不在于你是否&#34;切断&#34;成为一名程序员;这些技能可以学习。我个人使用的一个技巧是考虑他们所问的问题的预期结果究竟是什么?在这个特殊的例子中,正如我(我也认为你)在小学4年级学习,乘法重复加法。因此,我会实施它(并且在过去;我在几次访谈中也有同样的问题),并且循环重复添加。
问题是,如果你没有意识到乘法是重复添加(或者你要求回答的任何其他问题),那么你只是被搞砸了。这就是为什么我不是这些类型问题的忠实粉丝,因为很多问题都归结为你要么知道或不知道的琐事,而不是测试你作为程序员的真正技能(上面提到的关于库等的技能可以通过其他方式进行更好的测试。)
答案 3 :(得分:6)
TL; DR - 告诉采访者重新发明轮子是一个坏主意
我不会接受采访者的Connection Options for External Applications问题,而是以不同的方式回答采访问题:
Code Golf,Intel,AMD和其他微处理器制造商的辉煌工程师几十年来一直在为如何在尽可能少的周期内将32位整数相乘而苦恼,事实上,甚至能够产生正确的,完全64位的32位整数乘法结果而不会溢出。
(例如,不预先a
或b
到long
,增加2个整数,例如123456728 * 23456789
ARM)
在这方面,高级语言只有一个作业来处理像这样的整数乘法,即,以尽可能少的绒毛来完成处理器的工作。
在IMO软件中复制此类乘法的任何数量的Code Golf都是精神错乱。
毫无疑问,许多hacks
可以模拟乘法,但很多只会在有限范围的值a
和b
上工作(事实上,OP中列出的3种方法都没有对a和b的所有值执行无bug,即使我们忽略溢出问题)。并且所有都将比IMUL
指令慢(数量级)。
例如,如果a
或b
是overflows into a negative number,则可以通过日志将其他变量向左移位。
if (b == 2)
return a << 1;
if (b == 4)
return a << 2;
...
但这真的很乏味。
如果*
运算符在Java语言规范中一夜之间真正消失,那么我将使用包含乘法函数的现有库,例如: positive power of 2,出于同样的原因 - 多年来比我更聪明的批判性思维已经用于制作和测试这样的图书馆。
BigInteger.multiply
对于64位及更高位显然是可靠的,尽管将结果转换回32位int会再次引发溢出问题。
播放运营商* Code Golf
的问题OP问题中引用的所有3种解决方案都存在固有问题:
a
为负数,则无效。
for (int i = 0; i < a; i++) {
resultat += b;
}
对于a的任何负值都将返回0,因为永远不会满足循环连续条件
BigInteger.multiply()
multiplier(100, 1000000)
“main”java.lang.StackOverflowError
log10
的四舍五入错误(更不用说尝试记录任何数字的明显问题&lt; = 0)。 e.g。
multiplier(2389, 123123);
返回294140846
,但实际答案是294140847
(最后的数字9 x 3表示产品必须以7结尾)
当将双重结果重新转换回整数时,即使使用两个连续双精度除法运算符的答案也容易Tail Call Optimisation:
static double multiply(double a, double b) {
return 0 == (int)b
? 0.0
: a / (1 / b);
}
e.g。值(int)multiply(1, 93)
返回92,因为multiply
返回92.99999 ....这会被强制转换为32位整数。
当然,我们不需要提及其中许多算法都是O(N)
或更差,因此性能会非常糟糕。
答案 4 :(得分:2)
答案 5 :(得分:1)
如果您没有整数值,则可以利用其他数学属性来获得2个数字的乘积。有人已经提到log10
,所以这里有点模糊不清:
public double multiply(double x, double y) {
Vector3d vx = new Vector3d(x, 0, 0);
Vector3d vy = new Vector3d(0, y, 0);
Vector3d result = new Vector3d().cross(vx, vy);
return result.length();
}
答案 6 :(得分:1)
一种解决方案是使用逐位操作。这有点类似于之前提出的答案,但也消除了分裂。我们可以有类似的东西。我会使用C,因为我不太了解Java。
uint16_t multiply( uint16_t a, uint16_t b ) {
uint16_t i = 0;
uint16_t result = 0;
for (i = 0; i < 16; i++) {
if ( a & (1<<i) ) {
result += b << i;
}
}
return result;
}
答案 7 :(得分:1)
采访者提出的问题反映了他们的价值观。许多程序员都会奖励他们自己的解谜技巧和数学敏锐度,他们认为这些技能是最好的程序员。
他们错了。最好的程序员处理最重要的事情,而不是最有趣的事情;做出简单,无聊的技术选择;写清楚;想想用户;并远离愚蠢的弯路。我希望我有这些技能和倾向!
如果您可以执行其中的一些操作并生成工作代码,那么许多编程团队都需要您。你可能是超级巨星。
但是当你被难倒时,你应该怎么做?
提出澄清问题。 (“什么样的数字?”“这是什么样的编程语言没有乘法?”并且没有粗鲁:“为什么我这样做?”)如果你怀疑,问题只是一个愚蠢的与现实无关的谜题,这些问题不会产生有用的答案。但是常识和对“问题背后的问题”的渴望是重要的工程优点。
在糟糕的面试中你能做的最好的就是展示自己的优势。认识到他们取决于你的面试官;如果他们不这样做,那就是他们的损失。不要气馁。还有其他公司。
答案 8 :(得分:0)
视情况使用BigInteger.multiply
或BigDecimal.multiply
。