JavaScript区间最小公倍数查询程序
LCM代表最小公倍数,一组数字的最小公倍数是在所有能被该组所有数字整除的数字中,最小的一个。我们将看到完整的代码以及对给定问题的解释。在本文中,我们将实现用于区间LCM查询的JavaScript程序。
问题介绍
在这个问题中,我们得到一个包含整数的数组和另一个包含成对数字的查询数组,这些数字表示来自给定数组的范围,我们必须计算该给定范围的所有元素的LCM。例如:
如果给定数组是:[1, 2, 3, 4, 5, 6],而查询数组是:[[1,3], [2,5]],则对于第一个查询,元素为[2, 3, 4],LCM为12。
对于第二个查询,元素是[3, 4, 5, 6],LCM是60。
LCM和GCD之间的数学关系
为了找到GCD,我们有一个欧几里得公式,借助它我们可以以对数复杂度找到两个数字的GCD,并且LCM和GCD之间存在以下关系:
LCM and GCD of a given set A {a1, a2, a3 …. , an} is: LCM(A) * GCD(A) = (a1 * a2 * a3* … * an) OR LCM(A) = (a1 * a2 * a3 … * an) / GCD(A)
因此,我们将找到所有数字的GCD和乘积,然后从中,我们可以用O(1)的操作找到数字的LCM。
朴素方法
朴素方法是遍历查询数组,对于每个查询,找到给定范围内的元素的乘积和GCD。从这两个值中找到LCM并返回它。让我们在代码中实现它:
示例
// function to find the gcd of the given number function gcd(a,b){ if (a == 0) { return b; } else { return gcd(b%a,a); } } // function to find the lcm function lcmRange(arr, l, r){ // taking gcd as zero because gcd of 0 with any number is that same number var cur_gcd = 0 var product = 1 var cur_lcm = arr[l] for(var i = l+1 ;i <= r; i++) { product = cur_lcm * arr[i]; cur_gcd = gcd(cur_lcm, arr[i]) cur_lcm = product/cur_gcd } console.log("The LCM of the element in the given range from " + l + " to " + r + " is: " + cur_lcm); } // defining the array var arr = [ 1, 2, 3, 4, 5, 6] // defining the queries array var queries = [[1,3], [2,5]] // traversing over the array for(var i = 0; i< queries.length; i++){ lcmRange(arr,queries[i][0], queries[i][1]) }
时间和空间复杂度
上述代码的时间复杂度为O(Q*N*log(D)),其中Q是查询的数量,N是数组中的元素数量,D是数组中最大的数字。
上述代码的空间复杂度为O(1),因为我们没有使用任何额外的空间。
在上述程序中,如果查询的数量等于N,则其时间复杂度大于N2,这使得这种方法效率低下。让我们看看另一种方法:
线段树方法
线段树是一种高级数据结构,用于将问题划分为多个片段,然后根据2的幂将它们全部连接起来。这需要一些空间,但对于区间查询,可以在对数时间内产生结果。让我们看看它的代码:
示例
// defining maximum size var MAX = 1000 // makking tree var tree = new Array(4*MAX); // declaring new array var arr = new Array(MAX); // function for lcm function lcm(a, b){ return a*b/gcd(a,b); } // function for gcd function gcd(a, b){ if (a == 0) { return b } else{ return gcd(b%a,a); } } // Function to creata a segment tree function build(first, last, cur_node){ // base condition if (first == last){ tree[cur_node] = arr[first]; return; } var mid = (first + last)/2 mid = Math.floor(mid); // creating left and right segments build(first, mid, 2*cur_node); build(mid+1, last, 2*cur_node + 1); // build the parent for current var lcm_l = tree[2*cur_node]; var lcm_r = tree[2*cur_node+1]; tree[cur_node] = lcm(lcm_l, lcm_r); } // Function to make queries for array range function query(first, last, l, r, cur_node){ // section out of current range // 1 is safe to return if (last < l || first > r){ return 1; } // complete inside the current segment if (l <= first && r >= last) return tree[cur_node]; // partially inside the current segment var mid = (first+last)/2; mid = Math.floor(mid) var lcm_l = query(first, mid, l, r, 2*cur_node); var lcm_r = query(mid+1, last, l, r, 2*cur_node+1); return lcm(lcm_l, lcm_r); } // defining the array arr[0] = 1; arr[1] = 2; arr[2] = 3; arr[3] = 4; arr[4] = 5; arr[5] = 6; // build the segment tree build(0, 5, 1); // defining query array var queries = [[1,3], [2,5]] // traversing over the array for(var i = 0; i< queries.length; i++){ console.log("The LCM of the element in the given range from " + queries[i][0] + " to " + queries[i][1] + " is: " + query(0,5,queries[i][0],queries[i][1],1) ); }
结论
在本教程中,我们实现了一个JavaScript程序来查找区间LCM查询。我们看到了两种方法,一种是时间复杂度为O(Q*N*log(D))的朴素方法,另一种是通过实现时间复杂度为O(Q*log(N))的线段树。