Tìm các phần tử dòng, cột của mảng 2 chiều?

Liên hệ QC

Nguyễn Duy Tuân

Nghị Hách
Thành viên danh dự
Tham gia
13/6/06
Bài viết
4,772
Được thích
10,282
Giới tính
Nam
Nghề nghiệp
Giáo viên, CEO tại Bluesofts
Tôi có vấn đề khó mà chưa làm được, đưa lên đây mong các bạn giúp cho.

Giả sử có mảng A[2,3] là mảng A gồm 2 dòng và 3 cột. Mảng B[k] sẽ nhận lại các phần tử trong mảng A như sau:

B[0] = A[0,0]
B[1] = A[0,1]
B[2] = A[0,2]
B[3] = A[1,0]
B[4] = A[1,1]
B[5] = A[1,2] (A[m,n] )

k = chạy từ 0 đến 5 (2*3=6 phần tử)

(?) Có một yêu cầu. Khi cho biết k = một số bất kỳ, cần tìm ra tại đó tọa độ dòngcột của mảng A ?

(Nếu cho k=5 ta sẽ phải tìm ra dòng = 1 và cột = 2)

Cách giải chỉ là cách biến đổi thông thường, không dùng hàm. Rất mong được các bạn chỉ giúp.
 
Lần chỉnh sửa cuối:
Anh thử dùng hàm DIV và MOD
k (-1) MOD m = hàng
k (-1) DIV n = cột.
 
Giả sử có mảng A[3,2] là mảng A gồm 2 dòng và 3 cột. Mảng B sẽ nhận lại các phần tử trong mảng A như sau:
có nhầm k nhỉ, phải là 3 hàng (dòng), cột 2 chứ nhỉ, tức là có m hàng n cột (thường trong lập trình chỉ số hàng được đặt trước chứ) ngược nên hơi loằng ngoằng


TUY NHIÊN K SAO VẪN CÓ CÁCH

?) Có một yêu cầu. Khi cho biết k = một số bất kỳ, cần tìm ra tại đó tọa độ dòngcột của mảng A ?

(Nếu cho k=5 ta sẽ phải tìm ra dòng = 1 và cột = 2)

Cách giải chỉ là cách biến đổi thông thường, không dùng hàm. Rất mong được các bạn chỉ giúp.y
gọi i,j là hàng / côt thì m cột, n hàng

Hàng:
i= ((k+1) DIV m )+1

Cột:
j= (k+1) MOD n


 
Vâng, là m là dòng, n là cột. A[3,2] là bị viết ngược, phải là A[2,3].

Cách giải như các bác đưa ra chắc có lẽ là được nhưng như mình đã nói là
TuanVNUNI đã viết:
Cách giải chỉ là cách biến đổi thông thường, không dùng hàm. Rất mong được các bạn chỉ giúp.

Lý do mà mình khongmuoons dùng hàm toán tử MODDIV là vì:
+ Có thể có cách biến đổi thông thường, như là */+-^ thôi, mình nghĩ mãi mà vẫn chưa ra.
+ Vì là xử lý mảng dữ liệu lớn nếu dùng hàm để làm sẽ làm chậm máy rất nhiều.

Mong các bác tiếp tục tìm cách giúp cho.
 
Vâng, là m là dòng, n là cột. A[3,2] là bị viết ngược, phải là A[2,3].

Cách giải như các bác đưa ra chắc có lẽ là được nhưng như mình đã nói là


Lý do mà mình khongmuoons dùng hàm toán tử MODDIV là vì:
+ Có thể có cách biến đổi thông thường, như là */+-^ thôi, mình nghĩ mãi mà vẫn chưa ra.
+ Vì là xử lý mảng dữ liệu lớn nếu dùng hàm để làm sẽ làm chậm máy rất nhiều.

Mong các bác tiếp tục tìm cách giúp cho.

thế thì, trong VB thế này

k = 5
m = 2
n = 3

i = (k + 1) / n + 1
j = k + 1 - (i - 1) * m

lưu ý k,m,n,i,j phải khai báo là biến NGUYÊN khi đó phép toán (k + 1) / n chỉ lấy phần nguyên (trong VB và C++, Java đều thế trừ Pascal) cái này chắc Tuân biết rõ
 
Lần chỉnh sửa cuối:
thế thì, trong VB thế này

k = 5
m = 2
n = 3

i = (k + 1) / n + 1
j = k + 1 - (i - 1) * m

lưu ý k,m,n,i,j phải khai báo là biến NGUYÊN

Cảm ơn bác tigertiger. Công thức trên chỉ đúng với k=2 và k=5, các trường hợp khác bị sai. Bác thử nhập thử trên bảng tính Excel sẽ thấy ngay +-+-+-+.
 
Cảm ơn bác tigertiger. Công thức trên chỉ đúng với k=2 và k=5, các trường hợp khác bị sai. Bác thử nhập thử trên bảng tính Excel sẽ thấy ngay +-+-+-+.

uh, Ktra lại mới thấy sai

Công thức đây

i = k \ n + 1 'hang
j = k + 1 - (i - 1) * n 'cot

NÊn DÙng VB Function ktra

Public Function tinhij(m As Long, n As Long, k As Long) As String
Dim i As Long, j As Long

i = k \n + 1 'hang
j = k + 1 - (i - 1) * n 'cot

tinhij = i & " " & j
End Function
có nghĩa là vẫn phải dùng toán tử \ hi iiiiiiiii rút đỡ đc 1 phép toán MOD thôi
 
Lần chỉnh sửa cuối:
Vấn đề là DIV và MOD sẽ giải quyết triệt để yêu cầu của bài toán.
Còn lại là tìm cách cải thiện tốc độ của hai hàm này bằng các hàm cấp thấp hay cộng bit chẳng hạn.
Anh thử tham khảo ở wiki về modulo & quotient , floor ,hay Coding C xem sao.
 
Chỉ tính dựa trên n (số cột), không lệ thuộc vào m (số hàng)
Xem file Excel: chọn m và n trong cell C2 và D2, xem kết quả
 

File đính kèm

Chỉ tính dựa trên n (số cột), không lệ thuộc vào m (số hàng)
Xem file Excel: chọn m và n trong cell C2 và D2, xem kết quả


ở đây, chắc Tuan muốn hỏi công thức cho trong VBA, a ptm0412 ah

file anh là thuần excel rùi

-----------

Nhưng đúng là nếu k thích phép toán \ chỉ dùng / thì phải thêm LỆNH IF mới được - nhưng thế lại chậm mất rùi
 
Đó là cộng trừ nhân chia trong VBA, với m, n, k, i , j khai báo Integer đó chứ!
Rounddown là thể hiện phần nguyên trong Excel tương tự khai báo biến nguyên trong VBA thôi.
Bài #5 TigerTiger cũng làm thế mà.

Còn if là công cụ để xác định số phần tử và số TT phần tử của B(k) thôi. (nhằm mục đích làm biếng trong Excel, không muốn gõ tay). Ở ngoài đương nhiên biết số phần tử là m x n chứ!

File đính kèm mới sửa name cho công thức giống với VBA hơn, Tiger xem lại dùm.

Viết công thức VBA Tuân cũng sẽ test lại bằng Excel như vậy đấy
 
Vâng, ý của em chính là tìm thuật toán cho việc xác định phần tử i,j của mảng A khi đã biết m,n,k.

Cách làm của bác tigertiger nếu bỏ con số 1 đi thì sẽ đúng với kết quả khi các phần tử i và j bắt đầu chạy từ 0 chứ không phải là 1.

Như vậy cách làm của anh ptm0412tigertiger đều cho ra đúng kết quả vàcos thểcoi là như nhau về thuật toán.
Cách làm của tigertiger sau khi đã xóa số 1 là
i = k \ n + 1
thực chất toán tử chia \ là lấy số nguyên và tương đương với lệnh
i := k DIV n + 1; (trong Pascal)như anh LearnExcel cũng đã gợi ý.

Của anh ptm0412 là

i=ROUNDDOWN(k/n,0) // hàm ROUNDDOWN cũng là để lấy số nguyên, hoặc có thể dùng hàm INT()


Để tìm phần tử i - cột của mảng A, hình như chỉ có thể hàm hoặc toán tử lấy phần nguyên của k/n ?
Theo em thì nếu loại bỏ hàm cũng như toán tử lấy phần nguyên ( \ ; DIV ; ROUNDDOWN() ; INT()) thì sẽ tốt hơn về mặt tốc độ xử lý.

Cảm ơn các bác đã giú đỡ tận tình. Em sẽ dùng cách của các bác.

Em vẫn cảm thấy như có một phép tính chỉ dùng toán tử * / + - để tìm i ? Không biết mọi người có nghĩ thế không?

Ah, vấn đề em hỏi trên đây là vì em đang viết hàm xử lý mảng dữ liệu trong Excel. Trong VB hay các ngôn ngữ chúng ta vẫn dùng mảng 2 chiều A[m,n] để xử lý bảng dữ liệu hay khối dữ liệu kiểu ma trận rất dễ dàng. Có một vấn đề là máy tính lưu các mảng n chiều về dạng mảng 1 chiều - là một dãy các ô nhớ liên tiếp (như vấn đề em nói từ A[m,n]-->B[k] đó). Excel được lập trình bằng ngôn ngữ C++, cấu trúc lưu mảng dữ liệu (cấp thấp) của nó là mảng một chiều đó, không phải là 2 chiều như chúng ta thấy qua các đối tượng CELLS, RANGE, hay làm trong VBA đâu. Cái chúng ta nhìn và xử lý với các ô trong Excel theo dạng mảng 2 chiều là chương trình đã chuyển hóa cho chúng ta rồi.
 
Để em xem lại đã !

Em vửa post lên nhưng chưa chính xác. Để em kiểm tra lại đã !!!!!!!!!!!!!!!!!!!!!!
 
Lần chỉnh sửa cuối:
TuanVNUI thử file này xem nhé, không có hàm gì cả.
 

File đính kèm

Công thức rút gọn và cho các số i, j, k bắt đầu bằng 0:
i = k / n - 0.499
j = k - i * n

Dựa trên căn bản là VBA khi chuyển số thành số nguyên theo khai báo Integer, nó cũng ngấm ngầm làm tròn.
Tương đương với round(xxx,0) của Excel.
Số 0.499 thực chất là 0.5 - 0.001 nhằm bảo đảm các số k/ n nguyên không còn nguyên buộc phải làm tròn lên.
Số 0.001 này (tạm gọi là epsilon) càng nhỏ càng tốt vì công thức sẽ chỉ đúng trong phạm vi 1 số dòng (của B(k)}. Thí dụ nếu số epsilon là 0.1 thì dòng n x (1 - 0.1) +1 bắt đầu sai, hình như quy luật là :
số dòng đúng = n x (1 - epsilon)

Xem file kèm theo, 2 cột công thức Excel sai khi cho epsilon = 0.1
 

File đính kèm

TuanVNUI thử file này xem nhé, không có hàm gì cả.

Cảm ơn anh. Cách của anh rất hay nhưng vẫn chưa hẳn đúng anh ạ.
Trong thủ tục VBA của anh đã khai báo
Dim i as Integer , j as Integer

Như vậy i,j chỉ nhận các số nguyên nằm trong đoạn -32,768 to 32,767 , như thế nó không thể nhận các số lẻ.

Khi chạy công thức của anh là

i = (k - (n / 2) - 0.02) / n + 1
j = k - (i - 1) * n

được kết quả như sau:

Mã:
[B]k       i         j[/B]
1	1	1
2	1	2
3	1	3
4	2	1
5	2	2
6	2	3
7	3	1
8	3	2
9	3	3
10	4	1
11	4	2
12	4	3
Nếu em gán biếni, j như sau
Dim i As Double, j As Double

thì kết quả sẽ là:
Mã:
[B]k        i                j[/B]  
1	0.826666667	1.52
2	1.16		1.52
3	1.493333333	1.52
4	1.826666667	1.52
5	2.16		1.52
6	2.493333333	1.52
7	2.826666667	1.52
8	3.16		1.52
9	3.493333333	1.52
10	3.826666667	1.52
11	4.16		1.52
12	4.493333333	1.52
Vậy tại sao với 2 khai báo lại có 2 kết quả khác nhau như vậy?

+ Kết quả đúng là các số nguyên là do i, j được khai báo kiểu số nguyên (Integer) nên khi nhận kết quả của biểu thức tính toán, VB thực hiện việc ép kiểu cho phù hợp, như là số 0.826666667 được ép là 1, 2.493333333 ép về 2 (theo cách làm tròn số kiểu round up)

+ Kết quả khi khai báo i, j kiểu số Double thì cho ra kết quả đúng như biểu thức tính toán, và trường hợp này VB không phải ép kiểu vì i, j có kiểu phù hợp với kết quả tính toán mà nó nhận (số thực).

Việc VB ép kiểu cũng giống như ta dùng hàm Int, Round hay toán tử \. Kết quả tính toán không cho ra số nguyên.

Thực tế các biến i,j là kiểu số nguyên, nhưng em đã dùng kiểu Double để kiểm tra mà thôi, trong các ngôn ngữ lập trình khác khi khai báo kiểu nguyên mà kết quả biểu mà nó nhận về là số lẻ sẽ bị báo lỗi --> thuật toán vẫn chưa hẳn đúng.

Từ công thức của anh em lập thành công thức trên bảng tính thì chỉ đúng khi phải kết hợp hàm làm tròn Int, Round,...

Thực sự các công thức mà các anh cung cấp đều có thể cho chạy được, nhưng vì để tìm một thuật giải tốt nhất (chỉ là */+-^) nên em vẫn mong các bác tiếp tục giúp em.

Xin cảm ơn các bác nhiều!

(Liệu bài toán của em bắt buộc phải dùng hàm hay toán tử làm tròn khi tính i ? )
 
Lần chỉnh sửa cuối:
Bài trên mình cũng có nói:
Dựa trên căn bản là VBA khi chuyển số thành số nguyên theo khai báo Integer, nó cũng ngấm ngầm làm tròn.
ngấm ngầm làm tròn là ép kiểu còn gì! hi hi
mà lại chạy chậm hơn hàm Excel trên sheet nữa chứ.
 
Theo thiển ý của tôi, hầu hết các ngôn ngữ lập trình (C, C++, C#,Python, ADA) vẫn sử dụng toán tử lấy Modulo (MOD, %) hay quotient (\) đó thôi.
TRong C++, 5 toán tử cơ bản là ( +, -, *, /, % ) việc sử dụng Modulo cũng tự nhiên như dùng +,-,*,/ vậy.
From http://www.cplusplus.com/doc/tutorial/operators.html
Arithmetic operators ( +, -, *, /, % )
The five arithmetical operations supported by the C++ language are:
+ addition
- subtraction
* multiplication
/ division
% modulo

Ngoài ra: CPU có thể trực tiếp tính toán sử dụng các toán tử khác với mã nguồn... để tối ưu hóa.
Theo cuốn WRITE GREAT CODE, Vol. 2: Thinking Low-Level, Writing High-Level. Copyright © 2006 by Randall Hyde
Often, the CPU can directly compute some value using a different operator
than the source code specifies, thereby replacing a more complex (or stronger)
instruction with a simpler instruction. For example, a shift operation can
implement multiplication or division by a constant that is a power of 2 and
certain modulo (remainder) operations are possible using a bitwise and
instruction (the shift and and instructions generally execute much faster
than multiply and divide instructions). Most compiler optimizers are good at
recognizing such operations and replacing the more expensive computation
with a less expensive sequence of machine instructions.

Theo Wiki :
In devices and software that implement bitwise operations more efficiently than modulo, these alternative forms can result in faster calculations.

In the C programming language, compiling with heavy speed optimizations will typically (depending on compiler and hardware) automatically convert modulo operations to bitwise AND in the assembly file.

In some compilers, the modulo operation is implemented as mod(a, n) = a - n * floor(a / n). When performing both modulo and division on the same numbers, one can get the same result somewhat more efficiently by avoiding the actual modulo operator, and using the formula above on the result, avoiding an additional division operation.

Tôi thực sự chưa có kinh nghiệm trong việc này và cứ phân vân là liệu có bằng chứng nào chứng minh là dùng các toán tử MOD & \ chậm hơn là chỉ dùng +,-,*,/ hay không?
 
Lần chỉnh sửa cuối:
...
Tôi thực sự chưa có kinh nghiệm trong việc này và cứ phân vân là liệu có bằng chứng nào chứng minh là dùng các toán tử MOD & \ chậm hơn là chỉ dùng +,-,*,/ hay không?

Em nghĩ là có. Anh thử dùng các hàm để đo thời gian thực thi mã lệnh thì biết ngay. Em đã có một bài viết trên GPE về cách đo thời gian tại đây: http://www.giaiphapexcel.com/forum/showthread.php?t=4916
 
Em nghĩ là có. Anh thử dùng các hàm để đo thời gian thực thi mã lệnh thì biết ngay. Em đã có một bài viết trên GPE về cách đo thời gian tại đây: http://www.giaiphapexcel.com/forum/showthread.php?t=4916

Tuan ah, vấn đề là ở chỗ hãy cho mảng 2 chiều cũng xuất phát từ không (0) -> mọi thứ sẽ OK

khi đó sẽ dễ chuyển đổi hơn và chỉ cần tính + - * / thui

thế nhé
 
Web KT

Bài viết mới nhất

Back
Top Bottom